sparse.c revision 411619e921904b896eddae81c086c1f687c8304d
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 <stdlib.h> 18 19#include <sparse/sparse.h> 20 21#include "sparse_file.h" 22 23#include "output_file.h" 24#include "backed_block.h" 25#include "sparse_defs.h" 26 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(); 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 queue_data_block(s->backed_block_list, data, len, block); 57 58 return 0; 59} 60 61int sparse_file_add_fill(struct sparse_file *s, 62 uint32_t fill_val, unsigned int len, unsigned int block) 63{ 64 queue_fill_block(s->backed_block_list, fill_val, len, block); 65 66 return 0; 67} 68 69int sparse_file_add_file(struct sparse_file *s, 70 const char *filename, int64_t file_offset, unsigned int len, 71 unsigned int block) 72{ 73 queue_data_file(s->backed_block_list, filename, file_offset, len, block); 74 75 return 0; 76} 77 78struct count_chunks { 79 unsigned int chunks; 80 int64_t cur_ptr; 81 unsigned int block_size; 82}; 83 84static void count_data_block(void *priv, int64_t off, void *data, int len) 85{ 86 struct count_chunks *count_chunks = priv; 87 if (off > count_chunks->cur_ptr) 88 count_chunks->chunks++; 89 count_chunks->cur_ptr = off + ALIGN(len, count_chunks->block_size); 90 count_chunks->chunks++; 91} 92 93static void count_fill_block(void *priv, int64_t off, unsigned int fill_val, int len) 94{ 95 struct count_chunks *count_chunks = priv; 96 if (off > count_chunks->cur_ptr) 97 count_chunks->chunks++; 98 count_chunks->cur_ptr = off + ALIGN(len, count_chunks->block_size); 99 count_chunks->chunks++; 100} 101 102static void count_file_block(void *priv, int64_t off, const char *file, 103 int64_t offset, int len) 104{ 105 struct count_chunks *count_chunks = priv; 106 if (off > count_chunks->cur_ptr) 107 count_chunks->chunks++; 108 count_chunks->cur_ptr = off + ALIGN(len, count_chunks->block_size); 109 count_chunks->chunks++; 110} 111 112static int count_sparse_chunks(struct backed_block_list *b, 113 unsigned int block_size, int64_t len) 114{ 115 struct count_chunks count_chunks = {0, 0, block_size}; 116 117 for_each_data_block(b, count_data_block, count_file_block, 118 count_fill_block, &count_chunks, block_size); 119 120 if (count_chunks.cur_ptr != len) 121 count_chunks.chunks++; 122 123 return count_chunks.chunks; 124} 125 126static void ext4_write_data_block(void *priv, int64_t off, void *data, int len) 127{ 128 write_data_block(priv, off, data, len); 129} 130 131static void ext4_write_fill_block(void *priv, int64_t off, unsigned int fill_val, int len) 132{ 133 write_fill_block(priv, off, fill_val, len); 134} 135 136static void ext4_write_data_file(void *priv, int64_t off, const char *file, 137 int64_t offset, int len) 138{ 139 write_data_file(priv, off, file, offset, len); 140} 141 142int sparse_file_write(struct sparse_file *s, int fd, bool gz, bool sparse, 143 bool crc) 144{ 145 int chunks = count_sparse_chunks(s->backed_block_list, s->block_size, 146 s->len); 147 struct output_file *out = open_output_fd(fd, s->block_size, s->len, 148 gz, sparse, chunks, crc); 149 150 if (!out) 151 return -ENOMEM; 152 153 for_each_data_block(s->backed_block_list, ext4_write_data_block, 154 ext4_write_data_file, ext4_write_fill_block, out, s->block_size); 155 156 if (s->len) 157 pad_output_file(out, s->len); 158 159 close_output_file(out); 160 161 return 0; 162} 163