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 29 struct 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 48 void sparse_file_destroy(struct sparse_file *s) 49 { 50 backed_block_list_destroy(s->backed_block_list); 51 free(s); 52 } 53 54 int 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 60 int 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 66 int 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 74 int 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 } 80 unsigned 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 103 static 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 125 static 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 151 int 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 171 int 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 192 static 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 199 int64_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 223 static 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 268 out: 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 277 int 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 314 void sparse_file_verbose(struct sparse_file *s) 315 { 316 s->verbose = true; 317 } 318