• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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