Home | History | Annotate | Download | only in libsparse
      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 int sparse_file_write_block(struct output_file *out,
    105 		struct backed_block *bb)
    106 {
    107 	int ret = -EINVAL;
    108 
    109 	switch (backed_block_type(bb)) {
    110 	case BACKED_BLOCK_DATA:
    111 		ret = write_data_chunk(out, backed_block_len(bb), backed_block_data(bb));
    112 		break;
    113 	case BACKED_BLOCK_FILE:
    114 		ret = write_file_chunk(out, backed_block_len(bb),
    115 				       backed_block_filename(bb),
    116 				       backed_block_file_offset(bb));
    117 		break;
    118 	case BACKED_BLOCK_FD:
    119 		ret = write_fd_chunk(out, backed_block_len(bb),
    120 				     backed_block_fd(bb),
    121 				     backed_block_file_offset(bb));
    122 		break;
    123 	case BACKED_BLOCK_FILL:
    124 		ret = write_fill_chunk(out, backed_block_len(bb),
    125 				       backed_block_fill_val(bb));
    126 		break;
    127 	}
    128 
    129 	return ret;
    130 }
    131 
    132 static int write_all_blocks(struct sparse_file *s, struct output_file *out)
    133 {
    134 	struct backed_block *bb;
    135 	unsigned int last_block = 0;
    136 	int64_t pad;
    137 	int ret = 0;
    138 
    139 	for (bb = backed_block_iter_new(s->backed_block_list); bb;
    140 			bb = backed_block_iter_next(bb)) {
    141 		if (backed_block_block(bb) > last_block) {
    142 			unsigned int blocks = backed_block_block(bb) - last_block;
    143 			write_skip_chunk(out, (int64_t)blocks * s->block_size);
    144 		}
    145 		ret = sparse_file_write_block(out, bb);
    146 		if (ret)
    147 			return ret;
    148 		last_block = backed_block_block(bb) +
    149 				DIV_ROUND_UP(backed_block_len(bb), s->block_size);
    150 	}
    151 
    152 	pad = s->len - (int64_t)last_block * s->block_size;
    153 	assert(pad >= 0);
    154 	if (pad > 0) {
    155 		write_skip_chunk(out, pad);
    156 	}
    157 
    158 	return 0;
    159 }
    160 
    161 int sparse_file_write(struct sparse_file *s, int fd, bool gz, bool sparse,
    162 		bool crc)
    163 {
    164 	int ret;
    165 	int chunks;
    166 	struct output_file *out;
    167 
    168 	chunks = sparse_count_chunks(s);
    169 	out = output_file_open_fd(fd, s->block_size, s->len, gz, sparse, chunks, crc);
    170 
    171 	if (!out)
    172 		return -ENOMEM;
    173 
    174 	ret = write_all_blocks(s, out);
    175 
    176 	output_file_close(out);
    177 
    178 	return ret;
    179 }
    180 
    181 int sparse_file_callback(struct sparse_file *s, bool sparse, bool crc,
    182 		int (*write)(void *priv, const void *data, int len), void *priv)
    183 {
    184 	int ret;
    185 	int chunks;
    186 	struct output_file *out;
    187 
    188 	chunks = sparse_count_chunks(s);
    189 	out = output_file_open_callback(write, priv, s->block_size, s->len, false,
    190 			sparse, chunks, crc);
    191 
    192 	if (!out)
    193 		return -ENOMEM;
    194 
    195 	ret = write_all_blocks(s, out);
    196 
    197 	output_file_close(out);
    198 
    199 	return ret;
    200 }
    201 
    202 struct chunk_data {
    203 	void		*priv;
    204 	unsigned int	block;
    205 	unsigned int	nr_blocks;
    206 	int (*write)(void *priv, const void *data, int len, unsigned int block,
    207 		     unsigned int nr_blocks);
    208 };
    209 
    210 static int foreach_chunk_write(void *priv, const void *data, int len)
    211 {
    212 	struct chunk_data *chk = priv;
    213 
    214 	return chk->write(chk->priv, data, len, chk->block, chk->nr_blocks);
    215 }
    216 
    217 int sparse_file_foreach_chunk(struct sparse_file *s, bool sparse, bool crc,
    218 	int (*write)(void *priv, const void *data, int len, unsigned int block,
    219 		     unsigned int nr_blocks),
    220 	void *priv)
    221 {
    222 	int ret;
    223 	int chunks;
    224 	struct chunk_data chk;
    225 	struct output_file *out;
    226 	struct backed_block *bb;
    227 
    228 	chk.priv = priv;
    229 	chk.write = write;
    230 	chk.block = chk.nr_blocks = 0;
    231 	chunks = sparse_count_chunks(s);
    232 	out = output_file_open_callback(foreach_chunk_write, &chk,
    233 					s->block_size, s->len, false, sparse,
    234 					chunks, crc);
    235 
    236 	if (!out)
    237 		return -ENOMEM;
    238 
    239 	for (bb = backed_block_iter_new(s->backed_block_list); bb;
    240 			bb = backed_block_iter_next(bb)) {
    241 		chk.block = backed_block_block(bb);
    242 		chk.nr_blocks = (backed_block_len(bb) - 1) / s->block_size + 1;
    243 		ret = sparse_file_write_block(out, bb);
    244 		if (ret)
    245 			return ret;
    246 	}
    247 
    248 	output_file_close(out);
    249 
    250 	return ret;
    251 }
    252 
    253 static int out_counter_write(void *priv, const void *data __unused, int len)
    254 {
    255 	int64_t *count = priv;
    256 	*count += len;
    257 	return 0;
    258 }
    259 
    260 int64_t sparse_file_len(struct sparse_file *s, bool sparse, bool crc)
    261 {
    262 	int ret;
    263 	int chunks = sparse_count_chunks(s);
    264 	int64_t count = 0;
    265 	struct output_file *out;
    266 
    267 	out = output_file_open_callback(out_counter_write, &count,
    268 			s->block_size, s->len, false, sparse, chunks, crc);
    269 	if (!out) {
    270 		return -1;
    271 	}
    272 
    273 	ret = write_all_blocks(s, out);
    274 
    275 	output_file_close(out);
    276 
    277 	if (ret < 0) {
    278 		return -1;
    279 	}
    280 
    281 	return count;
    282 }
    283 
    284 unsigned int sparse_file_block_size(struct sparse_file *s)
    285 {
    286 	return s->block_size;
    287 }
    288 
    289 static struct backed_block *move_chunks_up_to_len(struct sparse_file *from,
    290 		struct sparse_file *to, unsigned int len)
    291 {
    292 	int64_t count = 0;
    293 	struct output_file *out_counter;
    294 	struct backed_block *last_bb = NULL;
    295 	struct backed_block *bb;
    296 	struct backed_block *start;
    297 	unsigned int last_block = 0;
    298 	int64_t file_len = 0;
    299 	int ret;
    300 
    301 	/*
    302 	 * overhead is sparse file header, the potential end skip
    303 	 * chunk and crc chunk.
    304 	 */
    305 	int overhead = sizeof(sparse_header_t) + 2 * sizeof(chunk_header_t) +
    306 			sizeof(uint32_t);
    307 	len -= overhead;
    308 
    309 	start = backed_block_iter_new(from->backed_block_list);
    310 	out_counter = output_file_open_callback(out_counter_write, &count,
    311 			to->block_size, to->len, false, true, 0, false);
    312 	if (!out_counter) {
    313 		return NULL;
    314 	}
    315 
    316 	for (bb = start; bb; bb = backed_block_iter_next(bb)) {
    317 		count = 0;
    318 		if (backed_block_block(bb) > last_block)
    319 			count += sizeof(chunk_header_t);
    320 		last_block = backed_block_block(bb) +
    321 				DIV_ROUND_UP(backed_block_len(bb), to->block_size);
    322 
    323 		/* will call out_counter_write to update count */
    324 		ret = sparse_file_write_block(out_counter, bb);
    325 		if (ret) {
    326 			bb = NULL;
    327 			goto out;
    328 		}
    329 		if (file_len + count > len) {
    330 			/*
    331 			 * If the remaining available size is more than 1/8th of the
    332 			 * requested size, split the chunk.  Results in sparse files that
    333 			 * are at least 7/8ths of the requested size
    334 			 */
    335 			file_len += sizeof(chunk_header_t);
    336 			if (!last_bb || (len - file_len > (len / 8))) {
    337 				backed_block_split(from->backed_block_list, bb, len - file_len);
    338 				last_bb = bb;
    339 			}
    340 			goto move;
    341 		}
    342 		file_len += count;
    343 		last_bb = bb;
    344 	}
    345 
    346 move:
    347 	backed_block_list_move(from->backed_block_list,
    348 		to->backed_block_list, start, last_bb);
    349 
    350 out:
    351 	output_file_close(out_counter);
    352 
    353 	return bb;
    354 }
    355 
    356 int sparse_file_resparse(struct sparse_file *in_s, unsigned int max_len,
    357 		struct sparse_file **out_s, int out_s_count)
    358 {
    359 	struct backed_block *bb;
    360 	struct sparse_file *s;
    361 	struct sparse_file *tmp;
    362 	int c = 0;
    363 
    364 	tmp = sparse_file_new(in_s->block_size, in_s->len);
    365 	if (!tmp) {
    366 		return -ENOMEM;
    367 	}
    368 
    369 	do {
    370 		s = sparse_file_new(in_s->block_size, in_s->len);
    371 
    372 		bb = move_chunks_up_to_len(in_s, s, max_len);
    373 
    374 		if (c < out_s_count) {
    375 			out_s[c] = s;
    376 		} else {
    377 			backed_block_list_move(s->backed_block_list, tmp->backed_block_list,
    378 					NULL, NULL);
    379 			sparse_file_destroy(s);
    380 		}
    381 		c++;
    382 	} while (bb);
    383 
    384 	backed_block_list_move(tmp->backed_block_list, in_s->backed_block_list,
    385 			NULL, NULL);
    386 
    387 	sparse_file_destroy(tmp);
    388 
    389 	return c;
    390 }
    391 
    392 void sparse_file_verbose(struct sparse_file *s)
    393 {
    394 	s->verbose = true;
    395 }
    396