1 /*
2 * Copyright (C) 2010 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 #define _LARGEFILE64_SOURCE
17
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <sys/types.h>
21 #include <sys/mman.h>
22 #include <unistd.h>
23 #include <fcntl.h>
24
25 #include <zlib.h>
26
27 #include "ext4_utils.h"
28 #include "output_file.h"
29 #include "sparse_format.h"
30 #include "sparse_crc32.h"
31
32 #if defined(__APPLE__) && defined(__MACH__)
33 #define lseek64 lseek
34 #define off64_t off_t
35 #endif
36
37 #define SPARSE_HEADER_MAJOR_VER 1
38 #define SPARSE_HEADER_MINOR_VER 0
39 #define SPARSE_HEADER_LEN (sizeof(sparse_header_t))
40 #define CHUNK_HEADER_LEN (sizeof(chunk_header_t))
41
42 struct output_file_ops {
43 int (*seek)(struct output_file *, off64_t);
44 int (*write)(struct output_file *, u8 *, int);
45 void (*close)(struct output_file *);
46 };
47
48 struct output_file {
49 int fd;
50 gzFile gz_fd;
51 int sparse;
52 u64 cur_out_ptr;
53 int chunk_cnt;
54 u32 crc32;
55 struct output_file_ops *ops;
56 };
57
file_seek(struct output_file * out,off64_t off)58 static int file_seek(struct output_file *out, off64_t off)
59 {
60 off64_t ret;
61
62 ret = lseek64(out->fd, off, SEEK_SET);
63 if (ret < 0) {
64 error_errno("lseek64");
65 return -1;
66 }
67 return 0;
68 }
69
file_write(struct output_file * out,u8 * data,int len)70 static int file_write(struct output_file *out, u8 *data, int len)
71 {
72 int ret;
73 ret = write(out->fd, data, len);
74 if (ret < 0) {
75 error_errno("write");
76 return -1;
77 } else if (ret < len) {
78 error("incomplete write");
79 return -1;
80 }
81
82 return 0;
83 }
84
file_close(struct output_file * out)85 static void file_close(struct output_file *out)
86 {
87 close(out->fd);
88 }
89
90
91 static struct output_file_ops file_ops = {
92 .seek = file_seek,
93 .write = file_write,
94 .close = file_close,
95 };
96
gz_file_seek(struct output_file * out,off64_t off)97 static int gz_file_seek(struct output_file *out, off64_t off)
98 {
99 off64_t ret;
100
101 ret = gzseek(out->gz_fd, off, SEEK_SET);
102 if (ret < 0) {
103 error_errno("gzseek");
104 return -1;
105 }
106 return 0;
107 }
108
gz_file_write(struct output_file * out,u8 * data,int len)109 static int gz_file_write(struct output_file *out, u8 *data, int len)
110 {
111 int ret;
112 ret = gzwrite(out->gz_fd, data, len);
113 if (ret < 0) {
114 error_errno("gzwrite");
115 return -1;
116 } else if (ret < len) {
117 error("incomplete gzwrite");
118 return -1;
119 }
120
121 return 0;
122 }
123
gz_file_close(struct output_file * out)124 static void gz_file_close(struct output_file *out)
125 {
126 gzclose(out->gz_fd);
127 }
128
129 static struct output_file_ops gz_file_ops = {
130 .seek = gz_file_seek,
131 .write = gz_file_write,
132 .close = gz_file_close,
133 };
134
135 static sparse_header_t sparse_header = {
136 .magic = SPARSE_HEADER_MAGIC,
137 .major_version = SPARSE_HEADER_MAJOR_VER,
138 .minor_version = SPARSE_HEADER_MINOR_VER,
139 .file_hdr_sz = SPARSE_HEADER_LEN,
140 .chunk_hdr_sz = CHUNK_HEADER_LEN,
141 .blk_sz = 0,
142 .total_blks = 0,
143 .total_chunks = 0,
144 .image_checksum = 0
145 };
146
147 static u8 *zero_buf;
148
emit_skip_chunk(struct output_file * out,u64 skip_len)149 static int emit_skip_chunk(struct output_file *out, u64 skip_len)
150 {
151 chunk_header_t chunk_header;
152 int ret, chunk;
153
154 //DBG printf("skip chunk: 0x%llx bytes\n", skip_len);
155
156 if (skip_len % info.block_size) {
157 error("don't care size %llu is not a multiple of the block size %u",
158 skip_len, info.block_size);
159 return -1;
160 }
161
162 /* We are skipping data, so emit a don't care chunk. */
163 chunk_header.chunk_type = CHUNK_TYPE_DONT_CARE;
164 chunk_header.reserved1 = 0;
165 chunk_header.chunk_sz = skip_len / info.block_size;
166 chunk_header.total_sz = CHUNK_HEADER_LEN;
167 ret = out->ops->write(out, (u8 *)&chunk_header, sizeof(chunk_header));
168 if (ret < 0)
169 return -1;
170
171 out->cur_out_ptr += skip_len;
172 out->chunk_cnt++;
173
174 /* Compute the CRC for all those zeroes. Do it block_size bytes at a time. */
175 while (skip_len) {
176 chunk = (skip_len > info.block_size) ? info.block_size : skip_len;
177 out->crc32 = sparse_crc32(out->crc32, zero_buf, chunk);
178 skip_len -= chunk;
179 }
180
181 return 0;
182 }
183
write_chunk_raw(struct output_file * out,u64 off,u8 * data,int len)184 static int write_chunk_raw(struct output_file *out, u64 off, u8 *data, int len)
185 {
186 chunk_header_t chunk_header;
187 int rnd_up_len, zero_len;
188 int ret;
189
190 /* We can assume that all the chunks to be written are in
191 * ascending order, block-size aligned, and non-overlapping.
192 * So, if the offset is less than the current output pointer,
193 * throw an error, and if there is a gap, emit a "don't care"
194 * chunk. The first write (of the super block) may not be
195 * blocksize aligned, so we need to deal with that too.
196 */
197 //DBG printf("write chunk: offset 0x%llx, length 0x%x bytes\n", off, len);
198
199 if (off < out->cur_out_ptr) {
200 error("offset %llu is less than the current output offset %llu",
201 off, out->cur_out_ptr);
202 return -1;
203 }
204
205 if (off > out->cur_out_ptr) {
206 emit_skip_chunk(out, off - out->cur_out_ptr);
207 }
208
209 if (off % info.block_size) {
210 error("write chunk offset %llu is not a multiple of the block size %u",
211 off, info.block_size);
212 return -1;
213 }
214
215 if (off != out->cur_out_ptr) {
216 error("internal error, offset accounting screwy in write_chunk_raw()");
217 return -1;
218 }
219
220 /* Round up the file length to a multiple of the block size */
221 rnd_up_len = (len + (info.block_size - 1)) & (~(info.block_size -1));
222 zero_len = rnd_up_len - len;
223
224 /* Finally we can safely emit a chunk of data */
225 chunk_header.chunk_type = CHUNK_TYPE_RAW;
226 chunk_header.reserved1 = 0;
227 chunk_header.chunk_sz = rnd_up_len / info.block_size;
228 chunk_header.total_sz = CHUNK_HEADER_LEN + rnd_up_len;
229 ret = out->ops->write(out, (u8 *)&chunk_header, sizeof(chunk_header));
230
231 if (ret < 0)
232 return -1;
233 ret = out->ops->write(out, data, len);
234 if (ret < 0)
235 return -1;
236 if (zero_len) {
237 ret = out->ops->write(out, zero_buf, zero_len);
238 if (ret < 0)
239 return -1;
240 }
241
242 out->crc32 = sparse_crc32(out->crc32, data, len);
243 if (zero_len)
244 out->crc32 = sparse_crc32(out->crc32, zero_buf, zero_len);
245 out->cur_out_ptr += rnd_up_len;
246 out->chunk_cnt++;
247
248 return 0;
249 }
250
close_output_file(struct output_file * out)251 void close_output_file(struct output_file *out)
252 {
253 int ret;
254
255 if (out->sparse) {
256 /* we need to seek back to the beginning and update the file header */
257 sparse_header.total_chunks = out->chunk_cnt;
258 sparse_header.image_checksum = out->crc32;
259
260 ret = out->ops->seek(out, 0);
261 if (ret < 0)
262 error("failure seeking to start of sparse file");
263
264 ret = out->ops->write(out, (u8 *)&sparse_header, sizeof(sparse_header));
265 if (ret < 0)
266 error("failure updating sparse file header");
267 }
268 out->ops->close(out);
269 }
270
open_output_file(const char * filename,int gz,int sparse)271 struct output_file *open_output_file(const char *filename, int gz, int sparse)
272 {
273 int ret;
274 struct output_file *out = malloc(sizeof(struct output_file));
275 if (!out) {
276 error_errno("malloc struct out");
277 return NULL;
278 }
279 zero_buf = malloc(info.block_size);
280 if (!zero_buf) {
281 error_errno("malloc zero_buf");
282 return NULL;
283 }
284 memset(zero_buf, '\0', info.block_size);
285
286 if (gz) {
287 out->ops = &gz_file_ops;
288 out->gz_fd = gzopen(filename, "wb9");
289 if (!out->gz_fd) {
290 error_errno("gzopen");
291 free(out);
292 return NULL;
293 }
294 } else {
295 out->ops = &file_ops;
296 out->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
297 if (out->fd < 0) {
298 error_errno("open");
299 free(out);
300 return NULL;
301 }
302 }
303 out->sparse = sparse;
304 out->cur_out_ptr = 0ll;
305 out->chunk_cnt = 0;
306
307 /* Initialize the crc32 value */
308 out->crc32 = 0;
309
310 if (out->sparse) {
311 /* Write out the file header. We'll update the unknown fields
312 * when we close the file.
313 */
314 sparse_header.blk_sz = info.block_size,
315 sparse_header.total_blks = info.len / info.block_size,
316 ret = out->ops->write(out, (u8 *)&sparse_header, sizeof(sparse_header));
317 if (ret < 0)
318 return NULL;
319 }
320
321 return out;
322 }
323
pad_output_file(struct output_file * out,u64 len)324 void pad_output_file(struct output_file *out, u64 len)
325 {
326 int ret;
327
328 if (len > info.len) {
329 error("attempted to pad file %llu bytes past end of filesystem",
330 len - info.len);
331 return;
332 }
333 if (out->sparse) {
334 /* We need to emit a DONT_CARE chunk to pad out the file if the
335 * cur_out_ptr is not already at the end of the filesystem.
336 * We also need to compute the CRC for it.
337 */
338 if (len < out->cur_out_ptr) {
339 error("attempted to pad file %llu bytes less than the current output pointer",
340 out->cur_out_ptr - len);
341 return;
342 }
343 if (len > out->cur_out_ptr) {
344 emit_skip_chunk(out, len - out->cur_out_ptr);
345 }
346 } else {
347 //KEN TODO: Fixme. If the filesystem image needs no padding,
348 // this will overwrite the last byte in the file with 0
349 // The answer is to do accounting like the sparse image
350 // code does and know if there is already data there.
351 ret = out->ops->seek(out, len - 1);
352 if (ret < 0)
353 return;
354
355 ret = out->ops->write(out, (u8*)"", 1);
356 if (ret < 0)
357 return;
358 }
359 }
360
361 /* Write a contiguous region of data blocks from a memory buffer */
write_data_block(struct output_file * out,u64 off,u8 * data,int len)362 void write_data_block(struct output_file *out, u64 off, u8 *data, int len)
363 {
364 int ret;
365
366 if (off + len > info.len) {
367 error("attempted to write block %llu past end of filesystem",
368 off + len - info.len);
369 return;
370 }
371
372 if (out->sparse) {
373 write_chunk_raw(out, off, data, len);
374 } else {
375 ret = out->ops->seek(out, off);
376 if (ret < 0)
377 return;
378
379 ret = out->ops->write(out, data, len);
380 if (ret < 0)
381 return;
382 }
383 }
384
385 /* Write a contiguous region of data blocks from a file */
write_data_file(struct output_file * out,u64 off,const char * file,off_t offset,int len)386 void write_data_file(struct output_file *out, u64 off, const char *file,
387 off_t offset, int len)
388 {
389 int ret;
390
391 if (off + len >= info.len) {
392 error("attempted to write block %llu past end of filesystem",
393 off + len - info.len);
394 return;
395 }
396
397 int file_fd = open(file, O_RDONLY);
398 if (file_fd < 0) {
399 error_errno("open");
400 return;
401 }
402
403 u8 *data = mmap(NULL, len, PROT_READ, MAP_SHARED, file_fd, offset);
404 if (data == MAP_FAILED) {
405 error_errno("mmap");
406 close(file_fd);
407 return;
408 }
409
410 if (out->sparse) {
411 write_chunk_raw(out, off, data, len);
412 } else {
413 ret = out->ops->seek(out, off);
414 if (ret < 0)
415 goto err;
416
417 ret = out->ops->write(out, data, len);
418 if (ret < 0)
419 goto err;
420 }
421
422 munmap(data, len);
423
424 close(file_fd);
425
426 err:
427 munmap(data, len);
428 close(file_fd);
429 }
430
431