blob: a6134c9d2f135b0e8befcbfa2417f12fef666ef0 [file] [log] [blame]
Colin Cross28fa5bc2012-05-20 13:28:05 -07001/*
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
Colin Cross411619e2012-04-24 18:51:42 -070035 s->backed_block_list = backed_block_list_new();
36 if (!s->backed_block_list) {
37 free(s);
38 return NULL;
39 }
Colin Cross28fa5bc2012-05-20 13:28:05 -070040
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{
Colin Cross411619e2012-04-24 18:51:42 -070049 backed_block_list_destroy(s->backed_block_list);
Colin Cross28fa5bc2012-05-20 13:28:05 -070050 free(s);
51}
52
53int sparse_file_add_data(struct sparse_file *s,
54 void *data, unsigned int len, unsigned int block)
55{
Colin Cross411619e2012-04-24 18:51:42 -070056 queue_data_block(s->backed_block_list, data, len, block);
Colin Cross28fa5bc2012-05-20 13:28:05 -070057
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{
Colin Cross411619e2012-04-24 18:51:42 -070064 queue_fill_block(s->backed_block_list, fill_val, len, block);
Colin Cross28fa5bc2012-05-20 13:28:05 -070065
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{
Colin Cross411619e2012-04-24 18:51:42 -070073 queue_data_file(s->backed_block_list, filename, file_offset, len, block);
Colin Cross28fa5bc2012-05-20 13:28:05 -070074
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
Colin Cross411619e2012-04-24 18:51:42 -0700112static int count_sparse_chunks(struct backed_block_list *b,
113 unsigned int block_size, int64_t len)
Colin Cross28fa5bc2012-05-20 13:28:05 -0700114{
115 struct count_chunks count_chunks = {0, 0, block_size};
116
Colin Cross411619e2012-04-24 18:51:42 -0700117 for_each_data_block(b, count_data_block, count_file_block,
118 count_fill_block, &count_chunks, block_size);
Colin Cross28fa5bc2012-05-20 13:28:05 -0700119
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{
Colin Cross411619e2012-04-24 18:51:42 -0700145 int chunks = count_sparse_chunks(s->backed_block_list, s->block_size,
146 s->len);
Colin Cross28fa5bc2012-05-20 13:28:05 -0700147 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
Colin Cross411619e2012-04-24 18:51:42 -0700153 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);
Colin Cross28fa5bc2012-05-20 13:28:05 -0700155
156 if (s->len)
157 pad_output_file(out, s->len);
158
159 close_output_file(out);
160
161 return 0;
162}