/src/e2fsprogs/lib/ext2fs/imager.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * image.c --- writes out the critical parts of the filesystem as a |
3 | | * flat file. |
4 | | * |
5 | | * Copyright (C) 2000 Theodore Ts'o. |
6 | | * |
7 | | * Note: this uses the POSIX IO interfaces, unlike most of the other |
8 | | * functions in this library. So sue me. |
9 | | * |
10 | | * %Begin-Header% |
11 | | * This file may be redistributed under the terms of the GNU Library |
12 | | * General Public License, version 2. |
13 | | * %End-Header% |
14 | | */ |
15 | | |
16 | | #include "config.h" |
17 | | #include <stdio.h> |
18 | | #include <string.h> |
19 | | #if HAVE_UNISTD_H |
20 | | #include <unistd.h> |
21 | | #endif |
22 | | #if HAVE_ERRNO_H |
23 | | #include <errno.h> |
24 | | #endif |
25 | | #include <fcntl.h> |
26 | | #include <time.h> |
27 | | #if HAVE_SYS_STAT_H |
28 | | #include <sys/stat.h> |
29 | | #endif |
30 | | #if HAVE_SYS_TYPES_H |
31 | | #include <sys/types.h> |
32 | | #endif |
33 | | |
34 | | #include "ext2_fs.h" |
35 | | #include "ext2fs.h" |
36 | | |
37 | | #ifndef HAVE_TYPE_SSIZE_T |
38 | | typedef int ssize_t; |
39 | | #endif |
40 | | |
41 | | /* |
42 | | * This function returns 1 if the specified block is all zeros |
43 | | */ |
44 | | static int check_zero_block(char *buf, int blocksize) |
45 | 0 | { |
46 | 0 | char *cp = buf; |
47 | 0 | int left = blocksize; |
48 | |
|
49 | 0 | while (left > 0) { |
50 | 0 | if (*cp++) |
51 | 0 | return 0; |
52 | 0 | left--; |
53 | 0 | } |
54 | 0 | return 1; |
55 | 0 | } |
56 | | |
57 | | /* |
58 | | * Write the inode table out as a single block. |
59 | | */ |
60 | 3.48k | #define BUF_BLOCKS 32 |
61 | | |
62 | | errcode_t ext2fs_image_inode_write(ext2_filsys fs, int fd, int flags) |
63 | 179 | { |
64 | 179 | dgrp_t group; |
65 | 179 | ssize_t left, c, d; |
66 | 179 | char *buf, *cp; |
67 | 179 | blk64_t blk; |
68 | 179 | ssize_t actual; |
69 | 179 | errcode_t retval; |
70 | 179 | ext2_loff_t r; |
71 | | |
72 | 179 | buf = malloc(fs->blocksize * BUF_BLOCKS); |
73 | 179 | if (!buf) |
74 | 0 | return ENOMEM; |
75 | | |
76 | 2.72k | for (group = 0; group < fs->group_desc_count; group++) { |
77 | 2.71k | blk = ext2fs_inode_table_loc(fs, group); |
78 | 2.71k | if (!blk) { |
79 | 6 | retval = EXT2_ET_MISSING_INODE_TABLE; |
80 | 6 | goto errout; |
81 | 6 | } |
82 | 2.70k | left = fs->inode_blocks_per_group; |
83 | 2.70k | if ((blk < fs->super->s_first_data_block) || |
84 | 2.70k | (blk + left - 1 >= ext2fs_blocks_count(fs->super))) { |
85 | 113 | retval = EXT2_ET_GDESC_BAD_INODE_TABLE; |
86 | 113 | goto errout; |
87 | 113 | } |
88 | 5.82k | while (left) { |
89 | 3.27k | c = BUF_BLOCKS; |
90 | 3.27k | if (c > left) |
91 | 2.17k | c = left; |
92 | 3.27k | retval = io_channel_read_blk64(fs->io, blk, c, buf); |
93 | 3.27k | if (retval) |
94 | 45 | goto errout; |
95 | 3.23k | cp = buf; |
96 | 6.46k | while (c) { |
97 | 3.23k | if (!(flags & IMAGER_FLAG_SPARSEWRITE)) { |
98 | 3.23k | d = c; |
99 | 3.23k | goto skip_sparse; |
100 | 3.23k | } |
101 | | /* Skip zero blocks */ |
102 | 0 | if (check_zero_block(cp, fs->blocksize)) { |
103 | 0 | c--; |
104 | 0 | blk++; |
105 | 0 | left--; |
106 | 0 | cp += fs->blocksize; |
107 | 0 | r = ext2fs_llseek(fd, fs->blocksize, |
108 | 0 | SEEK_CUR); |
109 | 0 | if (r < 0) { |
110 | 0 | retval = errno; |
111 | 0 | goto errout; |
112 | 0 | } |
113 | 0 | continue; |
114 | 0 | } |
115 | | /* Find non-zero blocks */ |
116 | 0 | for (d = 1; d < c; d++) { |
117 | 0 | if (check_zero_block(cp + |
118 | 0 | d * fs->blocksize, |
119 | 0 | fs->blocksize)) |
120 | 0 | break; |
121 | 0 | } |
122 | 3.23k | skip_sparse: |
123 | 3.23k | actual = write(fd, cp, d * fs->blocksize); |
124 | 3.23k | if (actual == -1) { |
125 | 0 | retval = errno; |
126 | 0 | goto errout; |
127 | 0 | } |
128 | 3.23k | if (actual != d * fs->blocksize) { |
129 | 0 | retval = EXT2_ET_SHORT_WRITE; |
130 | 0 | goto errout; |
131 | 0 | } |
132 | 3.23k | blk += d; |
133 | 3.23k | left -= d; |
134 | 3.23k | cp += d * fs->blocksize; |
135 | 3.23k | c -= d; |
136 | 3.23k | } |
137 | 3.23k | } |
138 | 2.59k | } |
139 | 15 | retval = 0; |
140 | | |
141 | 179 | errout: |
142 | 179 | free(buf); |
143 | 179 | return retval; |
144 | 15 | } |
145 | | |
146 | | /* |
147 | | * Read in the inode table and stuff it into place |
148 | | */ |
149 | | errcode_t ext2fs_image_inode_read(ext2_filsys fs, int fd, |
150 | | int flags EXT2FS_ATTR((unused))) |
151 | 13 | { |
152 | 13 | dgrp_t group; |
153 | 13 | ssize_t c, left; |
154 | 13 | char *buf; |
155 | 13 | blk64_t blk; |
156 | 13 | ssize_t actual; |
157 | 13 | errcode_t retval; |
158 | | |
159 | 13 | buf = malloc(fs->blocksize * BUF_BLOCKS); |
160 | 13 | if (!buf) |
161 | 0 | return ENOMEM; |
162 | | |
163 | 106 | for (group = 0; group < fs->group_desc_count; group++) { |
164 | 103 | blk = ext2fs_inode_table_loc(fs, group); |
165 | 103 | if (!blk) { |
166 | 1 | retval = EXT2_ET_MISSING_INODE_TABLE; |
167 | 1 | goto errout; |
168 | 1 | } |
169 | 102 | left = fs->inode_blocks_per_group; |
170 | 102 | while (left) { |
171 | 9 | c = BUF_BLOCKS; |
172 | 9 | if (c > left) |
173 | 3 | c = left; |
174 | 9 | actual = read(fd, buf, fs->blocksize * c); |
175 | 9 | if (actual == -1) { |
176 | 0 | retval = errno; |
177 | 0 | goto errout; |
178 | 0 | } |
179 | 9 | if (actual != fs->blocksize * c) { |
180 | 9 | retval = EXT2_ET_SHORT_READ; |
181 | 9 | goto errout; |
182 | 9 | } |
183 | 0 | retval = io_channel_write_blk64(fs->io, blk, c, buf); |
184 | 0 | if (retval) |
185 | 0 | goto errout; |
186 | | |
187 | 0 | blk += c; |
188 | 0 | left -= c; |
189 | 0 | } |
190 | 102 | } |
191 | 3 | retval = ext2fs_flush_icache(fs); |
192 | | |
193 | 13 | errout: |
194 | 13 | free(buf); |
195 | 13 | return retval; |
196 | 3 | } |
197 | | |
198 | | /* |
199 | | * Write out superblock and group descriptors |
200 | | */ |
201 | | errcode_t ext2fs_image_super_write(ext2_filsys fs, int fd, |
202 | | int flags EXT2FS_ATTR((unused))) |
203 | 8 | { |
204 | 8 | char *buf, *cp; |
205 | 8 | ssize_t actual; |
206 | 8 | errcode_t retval; |
207 | | #ifdef WORDS_BIGENDIAN |
208 | | unsigned int groups_per_block; |
209 | | struct ext2_group_desc *gdp; |
210 | | int j; |
211 | | #endif |
212 | | |
213 | 8 | if (fs->group_desc == NULL) |
214 | 0 | return EXT2_ET_NO_GDESC; |
215 | | |
216 | 8 | buf = malloc(fs->blocksize); |
217 | 8 | if (!buf) |
218 | 0 | return ENOMEM; |
219 | | |
220 | | /* |
221 | | * Write out the superblock |
222 | | */ |
223 | 8 | memset(buf, 0, fs->blocksize); |
224 | | #ifdef WORDS_BIGENDIAN |
225 | | /* |
226 | | * We're writing out superblock so let's convert |
227 | | * it to little endian and then back if needed |
228 | | */ |
229 | | ext2fs_swap_super(fs->super); |
230 | | memcpy(buf, fs->super, SUPERBLOCK_SIZE); |
231 | | ext2fs_swap_super(fs->super); |
232 | | #else |
233 | 8 | memcpy(buf, fs->super, SUPERBLOCK_SIZE); |
234 | 8 | #endif |
235 | 8 | actual = write(fd, buf, fs->blocksize); |
236 | 8 | if (actual == -1) { |
237 | 0 | retval = errno; |
238 | 0 | goto errout; |
239 | 0 | } |
240 | 8 | if (actual != (ssize_t) fs->blocksize) { |
241 | 0 | retval = EXT2_ET_SHORT_WRITE; |
242 | 0 | goto errout; |
243 | 0 | } |
244 | | |
245 | | /* |
246 | | * Now write out the block group descriptors |
247 | | */ |
248 | | |
249 | 8 | cp = (char *) fs->group_desc; |
250 | | |
251 | | #ifdef WORDS_BIGENDIAN |
252 | | /* |
253 | | * Convert group descriptors to little endian and back |
254 | | * if needed |
255 | | */ |
256 | | groups_per_block = EXT2_DESC_PER_BLOCK(fs->super); |
257 | | for (j=0; j < groups_per_block*fs->desc_blocks; j++) { |
258 | | gdp = ext2fs_group_desc(fs, fs->group_desc, j); |
259 | | if (gdp) |
260 | | ext2fs_swap_group_desc2(fs, gdp); |
261 | | } |
262 | | #endif |
263 | | |
264 | 8 | actual = write(fd, cp, (ssize_t)fs->blocksize * fs->desc_blocks); |
265 | | |
266 | | |
267 | | #ifdef WORDS_BIGENDIAN |
268 | | groups_per_block = EXT2_DESC_PER_BLOCK(fs->super); |
269 | | for (j=0; j < groups_per_block*fs->desc_blocks; j++) { |
270 | | gdp = ext2fs_group_desc(fs, fs->group_desc, j); |
271 | | if (gdp) |
272 | | ext2fs_swap_group_desc2(fs, gdp); |
273 | | } |
274 | | #endif |
275 | | |
276 | 8 | if (actual == -1) { |
277 | 0 | retval = errno; |
278 | 0 | goto errout; |
279 | 0 | } |
280 | 8 | if (actual != (ssize_t)(fs->blocksize * fs->desc_blocks)) { |
281 | 0 | retval = EXT2_ET_SHORT_WRITE; |
282 | 0 | goto errout; |
283 | 0 | } |
284 | | |
285 | 8 | retval = 0; |
286 | | |
287 | 8 | errout: |
288 | 8 | free(buf); |
289 | 8 | return retval; |
290 | 8 | } |
291 | | |
292 | | /* |
293 | | * Read the superblock and group descriptors and overwrite them. |
294 | | */ |
295 | | errcode_t ext2fs_image_super_read(ext2_filsys fs, int fd, |
296 | | int flags EXT2FS_ATTR((unused))) |
297 | 3 | { |
298 | 3 | char *buf; |
299 | 3 | ssize_t actual, size; |
300 | 3 | errcode_t retval; |
301 | | |
302 | 3 | size = (ssize_t)fs->blocksize * (fs->group_desc_count + 1); |
303 | 3 | buf = malloc(size); |
304 | 3 | if (!buf) |
305 | 0 | return ENOMEM; |
306 | | |
307 | | /* |
308 | | * Read it all in. |
309 | | */ |
310 | 3 | actual = read(fd, buf, size); |
311 | 3 | if (actual == -1) { |
312 | 0 | retval = errno; |
313 | 0 | goto errout; |
314 | 0 | } |
315 | 3 | if (actual != size) { |
316 | 3 | retval = EXT2_ET_SHORT_READ; |
317 | 3 | goto errout; |
318 | 3 | } |
319 | | |
320 | | /* |
321 | | * Now copy in the superblock and group descriptors |
322 | | */ |
323 | 0 | memcpy(fs->super, buf, SUPERBLOCK_SIZE); |
324 | |
|
325 | 0 | memcpy(fs->group_desc, buf + fs->blocksize, |
326 | 0 | (ssize_t)fs->blocksize * fs->group_desc_count); |
327 | |
|
328 | 0 | retval = 0; |
329 | |
|
330 | 3 | errout: |
331 | 3 | free(buf); |
332 | 3 | return retval; |
333 | 0 | } |
334 | | |
335 | | /* |
336 | | * Write the block/inode bitmaps. |
337 | | */ |
338 | | errcode_t ext2fs_image_bitmap_write(ext2_filsys fs, int fd, int flags) |
339 | 130 | { |
340 | 130 | ext2fs_generic_bitmap bmap; |
341 | 130 | errcode_t retval; |
342 | 130 | ssize_t actual; |
343 | 130 | size_t c; |
344 | 130 | __u64 itr, cnt, size, total_size; |
345 | 130 | char buf[1024]; |
346 | | |
347 | 130 | if (flags & IMAGER_FLAG_INODEMAP) { |
348 | 0 | if (!fs->inode_map) { |
349 | 0 | retval = ext2fs_read_inode_bitmap(fs); |
350 | 0 | if (retval) |
351 | 0 | return retval; |
352 | 0 | } |
353 | 0 | bmap = fs->inode_map; |
354 | 0 | itr = 1; |
355 | 0 | cnt = (__u64)EXT2_INODES_PER_GROUP(fs->super) * |
356 | 0 | fs->group_desc_count; |
357 | 0 | size = (EXT2_INODES_PER_GROUP(fs->super) / 8); |
358 | 130 | } else { |
359 | 130 | if (!fs->block_map) { |
360 | 130 | retval = ext2fs_read_block_bitmap(fs); |
361 | 130 | if (retval) |
362 | 19 | return retval; |
363 | 130 | } |
364 | 111 | bmap = fs->block_map; |
365 | 111 | itr = fs->super->s_first_data_block; |
366 | 111 | cnt = EXT2_GROUPS_TO_CLUSTERS(fs->super, fs->group_desc_count); |
367 | 111 | size = EXT2_CLUSTERS_PER_GROUP(fs->super) / 8; |
368 | 111 | } |
369 | 111 | total_size = size * fs->group_desc_count; |
370 | | |
371 | 59.1k | while (cnt > 0) { |
372 | 59.0k | size = sizeof(buf); |
373 | 59.0k | if (size > (cnt >> 3)) |
374 | 84 | size = (cnt >> 3); |
375 | 59.0k | if (size == 0) |
376 | 20 | break; |
377 | | |
378 | 59.0k | retval = ext2fs_get_generic_bmap_range(bmap, itr, |
379 | 59.0k | size << 3, buf); |
380 | 59.0k | if (retval) |
381 | 0 | return retval; |
382 | | |
383 | 59.0k | actual = write(fd, buf, size); |
384 | 59.0k | if (actual == -1) |
385 | 0 | return errno; |
386 | 59.0k | if (actual != (int) size) |
387 | 0 | return EXT2_ET_SHORT_READ; |
388 | | |
389 | 59.0k | itr += size << 3; |
390 | 59.0k | cnt -= size << 3; |
391 | 59.0k | } |
392 | | |
393 | 111 | size = total_size % fs->blocksize; |
394 | 111 | memset(buf, 0, sizeof(buf)); |
395 | 111 | if (size) { |
396 | 62 | size = fs->blocksize - size; |
397 | 248 | while (size) { |
398 | 186 | c = size; |
399 | 186 | if (c > (int) sizeof(buf)) |
400 | 124 | c = sizeof(buf); |
401 | 186 | actual = write(fd, buf, c); |
402 | 186 | if (actual < 0) |
403 | 0 | return errno; |
404 | 186 | if ((size_t) actual != c) |
405 | 0 | return EXT2_ET_SHORT_WRITE; |
406 | 186 | size -= c; |
407 | 186 | } |
408 | 62 | } |
409 | 111 | return 0; |
410 | 111 | } |
411 | | |
412 | | |
413 | | /* |
414 | | * Read the block/inode bitmaps. |
415 | | */ |
416 | | errcode_t ext2fs_image_bitmap_read(ext2_filsys fs, int fd, int flags) |
417 | 97 | { |
418 | 97 | ext2fs_generic_bitmap bmap; |
419 | 97 | errcode_t retval; |
420 | 97 | __u64 itr, cnt; |
421 | 97 | char buf[1024]; |
422 | 97 | unsigned int size; |
423 | 97 | ssize_t actual; |
424 | | |
425 | 97 | if (flags & IMAGER_FLAG_INODEMAP) { |
426 | 0 | if (!fs->inode_map) { |
427 | 0 | retval = ext2fs_read_inode_bitmap(fs); |
428 | 0 | if (retval) |
429 | 0 | return retval; |
430 | 0 | } |
431 | 0 | bmap = fs->inode_map; |
432 | 0 | itr = 1; |
433 | 0 | cnt = (__u64)EXT2_INODES_PER_GROUP(fs->super) * |
434 | 0 | fs->group_desc_count; |
435 | 0 | size = (EXT2_INODES_PER_GROUP(fs->super) / 8); |
436 | 97 | } else { |
437 | 97 | if (!fs->block_map) { |
438 | 97 | retval = ext2fs_read_block_bitmap(fs); |
439 | 97 | if (retval) |
440 | 9 | return retval; |
441 | 97 | } |
442 | 88 | bmap = fs->block_map; |
443 | 88 | itr = fs->super->s_first_data_block; |
444 | 88 | cnt = EXT2_GROUPS_TO_BLOCKS(fs->super, fs->group_desc_count); |
445 | 88 | size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8; |
446 | 88 | } |
447 | | |
448 | 88 | while (cnt > 0) { |
449 | 88 | size = sizeof(buf); |
450 | 88 | if (size > (cnt >> 3)) |
451 | 53 | size = (cnt >> 3); |
452 | 88 | if (size == 0) |
453 | 0 | break; |
454 | | |
455 | 88 | actual = read(fd, buf, size); |
456 | 88 | if (actual == -1) |
457 | 0 | return errno; |
458 | 88 | if (actual != (int) size) |
459 | 88 | return EXT2_ET_SHORT_READ; |
460 | | |
461 | 0 | retval = ext2fs_set_generic_bmap_range(bmap, itr, |
462 | 0 | size << 3, buf); |
463 | 0 | if (retval) |
464 | 0 | return retval; |
465 | | |
466 | 0 | itr += size << 3; |
467 | 0 | cnt -= size << 3; |
468 | 0 | } |
469 | 0 | return 0; |
470 | 88 | } |