sparse.c revision 1e17b313a6257b7b5081e178e81435c09d60378e
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 102static void sparse_file_write_block(struct output_file *out, 103 struct backed_block *bb) 104{ 105 switch (backed_block_type(bb)) { 106 case BACKED_BLOCK_DATA: 107 write_data_chunk(out, backed_block_len(bb), backed_block_data(bb)); 108 break; 109 case BACKED_BLOCK_FILE: 110 write_file_chunk(out, backed_block_len(bb), 111 backed_block_filename(bb), backed_block_file_offset(bb)); 112 break; 113 case BACKED_BLOCK_FD: 114 write_fd_chunk(out, backed_block_len(bb), 115 backed_block_fd(bb), backed_block_file_offset(bb)); 116 break; 117 case BACKED_BLOCK_FILL: 118 write_fill_chunk(out, backed_block_len(bb), 119 backed_block_fill_val(bb)); 120 break; 121 } 122} 123 124static int write_all_blocks(struct sparse_file *s, struct output_file *out) 125{ 126 struct backed_block *bb; 127 unsigned int last_block = 0; 128 int64_t pad; 129 130 for (bb = backed_block_iter_new(s->backed_block_list); bb; 131 bb = backed_block_iter_next(bb)) { 132 if (backed_block_block(bb) > last_block) { 133 unsigned int blocks = backed_block_block(bb) - last_block; 134 write_skip_chunk(out, (int64_t)blocks * s->block_size); 135 } 136 sparse_file_write_block(out, bb); 137 last_block = backed_block_block(bb) + 138 DIV_ROUND_UP(backed_block_len(bb), s->block_size); 139 } 140 141 pad = s->len - last_block * s->block_size; 142 assert(pad >= 0); 143 if (pad > 0) { 144 write_skip_chunk(out, pad); 145 } 146 147 return 0; 148} 149 150int sparse_file_write(struct sparse_file *s, int fd, bool gz, bool sparse, 151 bool crc) 152{ 153 int ret; 154 int chunks; 155 struct output_file *out; 156 157 chunks = sparse_count_chunks(s); 158 out = open_output_fd(fd, s->block_size, s->len, gz, sparse, chunks, crc); 159 160 if (!out) 161 return -ENOMEM; 162 163 ret = write_all_blocks(s, out); 164 165 close_output_file(out); 166 167 return ret; 168} 169 170int sparse_file_callback(struct sparse_file *s, bool sparse, bool crc, 171 int (*write)(void *priv, const void *data, int len), void *priv) 172{ 173 int ret; 174 int chunks; 175 struct output_file *out; 176 177 chunks = sparse_count_chunks(s); 178 out = open_output_callback(write, priv, s->block_size, s->len, false, 179 sparse, chunks, crc); 180 181 if (!out) 182 return -ENOMEM; 183 184 ret = write_all_blocks(s, out); 185 186 close_output_file(out); 187 188 return ret; 189} 190 191void sparse_file_verbose(struct sparse_file *s) 192{ 193 s->verbose = true; 194} 195