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