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 */
check_zero_block(char * buf,int blocksize)44 static int check_zero_block(char *buf, int blocksize)
45 {
46 char *cp = buf;
47 int left = blocksize;
48
49 while (left > 0) {
50 if (*cp++)
51 return 0;
52 left--;
53 }
54 return 1;
55 }
56
57 /*
58 * Write the inode table out as a single block.
59 */
60 #define BUF_BLOCKS 32
61
ext2fs_image_inode_write(ext2_filsys fs,int fd,int flags)62 errcode_t ext2fs_image_inode_write(ext2_filsys fs, int fd, int flags)
63 {
64 dgrp_t group;
65 ssize_t left, c, d;
66 char *buf, *cp;
67 blk64_t blk;
68 ssize_t actual;
69 errcode_t retval;
70 ext2_loff_t r;
71
72 buf = malloc(fs->blocksize * BUF_BLOCKS);
73 if (!buf)
74 return ENOMEM;
75
76 for (group = 0; group < fs->group_desc_count; group++) {
77 blk = ext2fs_inode_table_loc(fs, group);
78 if (!blk) {
79 retval = EXT2_ET_MISSING_INODE_TABLE;
80 goto errout;
81 }
82 left = fs->inode_blocks_per_group;
83 if ((blk < fs->super->s_first_data_block) ||
84 (blk + left - 1 >= ext2fs_blocks_count(fs->super))) {
85 retval = EXT2_ET_GDESC_BAD_INODE_TABLE;
86 goto errout;
87 }
88 while (left) {
89 c = BUF_BLOCKS;
90 if (c > left)
91 c = left;
92 retval = io_channel_read_blk64(fs->io, blk, c, buf);
93 if (retval)
94 goto errout;
95 cp = buf;
96 while (c) {
97 if (!(flags & IMAGER_FLAG_SPARSEWRITE)) {
98 d = c;
99 goto skip_sparse;
100 }
101 /* Skip zero blocks */
102 if (check_zero_block(cp, fs->blocksize)) {
103 c--;
104 blk++;
105 left--;
106 cp += fs->blocksize;
107 r = ext2fs_llseek(fd, fs->blocksize,
108 SEEK_CUR);
109 if (r < 0) {
110 retval = errno;
111 goto errout;
112 }
113 continue;
114 }
115 /* Find non-zero blocks */
116 for (d = 1; d < c; d++) {
117 if (check_zero_block(cp +
118 d * fs->blocksize,
119 fs->blocksize))
120 break;
121 }
122 skip_sparse:
123 actual = write(fd, cp, d * fs->blocksize);
124 if (actual == -1) {
125 retval = errno;
126 goto errout;
127 }
128 if (actual != d * fs->blocksize) {
129 retval = EXT2_ET_SHORT_WRITE;
130 goto errout;
131 }
132 blk += d;
133 left -= d;
134 cp += d * fs->blocksize;
135 c -= d;
136 }
137 }
138 }
139 retval = 0;
140
141 errout:
142 free(buf);
143 return retval;
144 }
145
146 /*
147 * Read in the inode table and stuff it into place
148 */
ext2fs_image_inode_read(ext2_filsys fs,int fd,int flags EXT2FS_ATTR ((unused)))149 errcode_t ext2fs_image_inode_read(ext2_filsys fs, int fd,
150 int flags EXT2FS_ATTR((unused)))
151 {
152 dgrp_t group;
153 ssize_t c, left;
154 char *buf;
155 blk64_t blk;
156 ssize_t actual;
157 errcode_t retval;
158
159 buf = malloc(fs->blocksize * BUF_BLOCKS);
160 if (!buf)
161 return ENOMEM;
162
163 for (group = 0; group < fs->group_desc_count; group++) {
164 blk = ext2fs_inode_table_loc(fs, group);
165 if (!blk) {
166 retval = EXT2_ET_MISSING_INODE_TABLE;
167 goto errout;
168 }
169 left = fs->inode_blocks_per_group;
170 while (left) {
171 c = BUF_BLOCKS;
172 if (c > left)
173 c = left;
174 actual = read(fd, buf, fs->blocksize * c);
175 if (actual == -1) {
176 retval = errno;
177 goto errout;
178 }
179 if (actual != fs->blocksize * c) {
180 retval = EXT2_ET_SHORT_READ;
181 goto errout;
182 }
183 retval = io_channel_write_blk64(fs->io, blk, c, buf);
184 if (retval)
185 goto errout;
186
187 blk += c;
188 left -= c;
189 }
190 }
191 retval = ext2fs_flush_icache(fs);
192
193 errout:
194 free(buf);
195 return retval;
196 }
197
198 /*
199 * Write out superblock and group descriptors
200 */
ext2fs_image_super_write(ext2_filsys fs,int fd,int flags EXT2FS_ATTR ((unused)))201 errcode_t ext2fs_image_super_write(ext2_filsys fs, int fd,
202 int flags EXT2FS_ATTR((unused)))
203 {
204 char *buf, *cp;
205 ssize_t actual;
206 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 if (fs->group_desc == NULL)
214 return EXT2_ET_NO_GDESC;
215
216 buf = malloc(fs->blocksize);
217 if (!buf)
218 return ENOMEM;
219
220 /*
221 * Write out the superblock
222 */
223 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 memcpy(buf, fs->super, SUPERBLOCK_SIZE);
234 #endif
235 actual = write(fd, buf, fs->blocksize);
236 if (actual == -1) {
237 retval = errno;
238 goto errout;
239 }
240 if (actual != (ssize_t) fs->blocksize) {
241 retval = EXT2_ET_SHORT_WRITE;
242 goto errout;
243 }
244
245 /*
246 * Now write out the block group descriptors
247 */
248
249 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 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 if (actual == -1) {
277 retval = errno;
278 goto errout;
279 }
280 if (actual != (ssize_t)(fs->blocksize * fs->desc_blocks)) {
281 retval = EXT2_ET_SHORT_WRITE;
282 goto errout;
283 }
284
285 retval = 0;
286
287 errout:
288 free(buf);
289 return retval;
290 }
291
292 /*
293 * Read the superblock and group descriptors and overwrite them.
294 */
ext2fs_image_super_read(ext2_filsys fs,int fd,int flags EXT2FS_ATTR ((unused)))295 errcode_t ext2fs_image_super_read(ext2_filsys fs, int fd,
296 int flags EXT2FS_ATTR((unused)))
297 {
298 char *buf;
299 ssize_t actual, size;
300 errcode_t retval;
301
302 size = (ssize_t)fs->blocksize * (fs->group_desc_count + 1);
303 buf = malloc(size);
304 if (!buf)
305 return ENOMEM;
306
307 /*
308 * Read it all in.
309 */
310 actual = read(fd, buf, size);
311 if (actual == -1) {
312 retval = errno;
313 goto errout;
314 }
315 if (actual != size) {
316 retval = EXT2_ET_SHORT_READ;
317 goto errout;
318 }
319
320 /*
321 * Now copy in the superblock and group descriptors
322 */
323 memcpy(fs->super, buf, SUPERBLOCK_SIZE);
324
325 memcpy(fs->group_desc, buf + fs->blocksize,
326 (ssize_t)fs->blocksize * fs->group_desc_count);
327
328 retval = 0;
329
330 errout:
331 free(buf);
332 return retval;
333 }
334
335 /*
336 * Write the block/inode bitmaps.
337 */
ext2fs_image_bitmap_write(ext2_filsys fs,int fd,int flags)338 errcode_t ext2fs_image_bitmap_write(ext2_filsys fs, int fd, int flags)
339 {
340 ext2fs_generic_bitmap bmap;
341 errcode_t retval;
342 ssize_t actual;
343 size_t c;
344 __u64 itr, cnt, size, total_size;
345 char buf[1024];
346
347 if (flags & IMAGER_FLAG_INODEMAP) {
348 if (!fs->inode_map) {
349 retval = ext2fs_read_inode_bitmap(fs);
350 if (retval)
351 return retval;
352 }
353 bmap = fs->inode_map;
354 itr = 1;
355 cnt = (__u64)EXT2_INODES_PER_GROUP(fs->super) *
356 fs->group_desc_count;
357 size = (EXT2_INODES_PER_GROUP(fs->super) / 8);
358 } else {
359 if (!fs->block_map) {
360 retval = ext2fs_read_block_bitmap(fs);
361 if (retval)
362 return retval;
363 }
364 bmap = fs->block_map;
365 itr = fs->super->s_first_data_block;
366 cnt = EXT2_GROUPS_TO_CLUSTERS(fs->super, fs->group_desc_count);
367 size = EXT2_CLUSTERS_PER_GROUP(fs->super) / 8;
368 }
369 total_size = size * fs->group_desc_count;
370
371 while (cnt > 0) {
372 size = sizeof(buf);
373 if (size > (cnt >> 3))
374 size = (cnt >> 3);
375 if (size == 0)
376 break;
377
378 retval = ext2fs_get_generic_bmap_range(bmap, itr,
379 size << 3, buf);
380 if (retval)
381 return retval;
382
383 actual = write(fd, buf, size);
384 if (actual == -1)
385 return errno;
386 if (actual != (int) size)
387 return EXT2_ET_SHORT_READ;
388
389 itr += size << 3;
390 cnt -= size << 3;
391 }
392
393 size = total_size % fs->blocksize;
394 memset(buf, 0, sizeof(buf));
395 if (size) {
396 size = fs->blocksize - size;
397 while (size) {
398 c = size;
399 if (c > (int) sizeof(buf))
400 c = sizeof(buf);
401 actual = write(fd, buf, c);
402 if (actual < 0)
403 return errno;
404 if ((size_t) actual != c)
405 return EXT2_ET_SHORT_WRITE;
406 size -= c;
407 }
408 }
409 return 0;
410 }
411
412
413 /*
414 * Read the block/inode bitmaps.
415 */
ext2fs_image_bitmap_read(ext2_filsys fs,int fd,int flags)416 errcode_t ext2fs_image_bitmap_read(ext2_filsys fs, int fd, int flags)
417 {
418 ext2fs_generic_bitmap bmap;
419 errcode_t retval;
420 __u64 itr, cnt;
421 char buf[1024];
422 unsigned int size;
423 ssize_t actual;
424
425 if (flags & IMAGER_FLAG_INODEMAP) {
426 if (!fs->inode_map) {
427 retval = ext2fs_read_inode_bitmap(fs);
428 if (retval)
429 return retval;
430 }
431 bmap = fs->inode_map;
432 itr = 1;
433 cnt = (__u64)EXT2_INODES_PER_GROUP(fs->super) *
434 fs->group_desc_count;
435 size = (EXT2_INODES_PER_GROUP(fs->super) / 8);
436 } else {
437 if (!fs->block_map) {
438 retval = ext2fs_read_block_bitmap(fs);
439 if (retval)
440 return retval;
441 }
442 bmap = fs->block_map;
443 itr = fs->super->s_first_data_block;
444 cnt = EXT2_GROUPS_TO_BLOCKS(fs->super, fs->group_desc_count);
445 size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
446 }
447
448 while (cnt > 0) {
449 size = sizeof(buf);
450 if (size > (cnt >> 3))
451 size = (cnt >> 3);
452 if (size == 0)
453 break;
454
455 actual = read(fd, buf, size);
456 if (actual == -1)
457 return errno;
458 if (actual != (int) size)
459 return EXT2_ET_SHORT_READ;
460
461 retval = ext2fs_set_generic_bmap_range(bmap, itr,
462 size << 3, buf);
463 if (retval)
464 return retval;
465
466 itr += size << 3;
467 cnt -= size << 3;
468 }
469 return 0;
470 }
471