sparse.c revision be8ddcb35a459481c0bcf5bfe645c1fefe963f5c
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 28struct sparse_file *sparse_file_new(unsigned int block_size, int64_t len) 29{ 30 struct sparse_file *s = calloc(sizeof(struct sparse_file), 1); 31 if (!s) { 32 return NULL; 33 } 34 35 s->backed_block_list = backed_block_list_new(block_size); 36 if (!s->backed_block_list) { 37 free(s); 38 return NULL; 39 } 40 41 s->block_size = block_size; 42 s->len = len; 43 44 return s; 45} 46 47void sparse_file_destroy(struct sparse_file *s) 48{ 49 backed_block_list_destroy(s->backed_block_list); 50 free(s); 51} 52 53int sparse_file_add_data(struct sparse_file *s, 54 void *data, unsigned int len, unsigned int block) 55{ 56 return backed_block_add_data(s->backed_block_list, data, len, block); 57} 58 59int sparse_file_add_fill(struct sparse_file *s, 60 uint32_t fill_val, unsigned int len, unsigned int block) 61{ 62 return backed_block_add_fill(s->backed_block_list, fill_val, len, block); 63} 64 65int sparse_file_add_file(struct sparse_file *s, 66 const char *filename, int64_t file_offset, unsigned int len, 67 unsigned int block) 68{ 69 return backed_block_add_file(s->backed_block_list, filename, file_offset, 70 len, block); 71} 72 73int sparse_file_add_fd(struct sparse_file *s, 74 int fd, int64_t file_offset, unsigned int len, unsigned int block) 75{ 76 return backed_block_add_fd(s->backed_block_list, fd, file_offset, 77 len, block); 78} 79unsigned int sparse_count_chunks(struct sparse_file *s) 80{ 81 struct backed_block *bb; 82 unsigned int last_block = 0; 83 unsigned int chunks = 0; 84 85 for (bb = backed_block_iter_new(s->backed_block_list); bb; 86 bb = backed_block_iter_next(bb)) { 87 if (backed_block_block(bb) > last_block) { 88 /* If there is a gap between chunks, add a skip chunk */ 89 chunks++; 90 } 91 chunks++; 92 last_block = backed_block_block(bb) + 93 DIV_ROUND_UP(backed_block_len(bb), s->block_size); 94 } 95 if (last_block < DIV_ROUND_UP(s->len, s->block_size)) { 96 chunks++; 97 } 98 99 return chunks; 100} 101 102int sparse_file_write(struct sparse_file *s, int fd, bool gz, bool sparse, 103 bool crc) 104{ 105 struct backed_block *bb; 106 unsigned int last_block = 0; 107 int64_t pad; 108 int chunks; 109 struct output_file *out; 110 111 chunks = sparse_count_chunks(s); 112 out = open_output_fd(fd, s->block_size, s->len, gz, sparse, chunks, crc); 113 114 if (!out) 115 return -ENOMEM; 116 117 for (bb = backed_block_iter_new(s->backed_block_list); bb; 118 bb = backed_block_iter_next(bb)) { 119 if (backed_block_block(bb) > last_block) { 120 unsigned int blocks = backed_block_block(bb) - last_block; 121 write_skip_chunk(out, (int64_t)blocks * s->block_size); 122 } 123 switch (backed_block_type(bb)) { 124 case BACKED_BLOCK_DATA: 125 write_data_chunk(out, backed_block_len(bb), backed_block_data(bb)); 126 break; 127 case BACKED_BLOCK_FILE: 128 write_file_chunk(out, backed_block_len(bb), 129 backed_block_filename(bb), backed_block_file_offset(bb)); 130 break; 131 case BACKED_BLOCK_FD: 132 write_fd_chunk(out, backed_block_len(bb), 133 backed_block_fd(bb), backed_block_file_offset(bb)); 134 break; 135 case BACKED_BLOCK_FILL: 136 write_fill_chunk(out, backed_block_len(bb), 137 backed_block_fill_val(bb)); 138 break; 139 } 140 last_block = backed_block_block(bb) + 141 DIV_ROUND_UP(backed_block_len(bb), s->block_size); 142 } 143 144 pad = s->len - last_block * s->block_size; 145 assert(pad >= 0); 146 if (pad > 0) { 147 write_skip_chunk(out, pad); 148 } 149 150 close_output_file(out); 151 152 return 0; 153} 154 155void sparse_file_verbose(struct sparse_file *s) 156{ 157 s->verbose = true; 158} 159