1 /*
2 * e2image.c --- Program which writes an image file backing up
3 * critical metadata for the filesystem.
4 *
5 * Copyright 2000, 2001 by Theodore Ts'o.
6 *
7 * %Begin-Header%
8 * This file may be redistributed under the terms of the GNU Public
9 * License.
10 * %End-Header%
11 */
12
13 #define _LARGEFILE_SOURCE
14 #define _LARGEFILE64_SOURCE
15
16 #include <fcntl.h>
17 #include <grp.h>
18 #ifdef HAVE_GETOPT_H
19 #include <getopt.h>
20 #else
21 extern char *optarg;
22 extern int optind;
23 #endif
24 #include <pwd.h>
25 #include <stdio.h>
26 #ifdef HAVE_STDLIB_H
27 #include <stdlib.h>
28 #endif
29 #include <string.h>
30 #include <time.h>
31 #include <unistd.h>
32 #include <fcntl.h>
33 #include <errno.h>
34 #include <sys/stat.h>
35 #include <sys/types.h>
36
37 #include "ext2fs/ext2_fs.h"
38 #include "ext2fs/ext2fs.h"
39 #include "et/com_err.h"
40 #include "uuid/uuid.h"
41 #include "e2p/e2p.h"
42 #include "ext2fs/e2image.h"
43
44 #include "../version.h"
45 #include "nls-enable.h"
46
47 const char * program_name = "e2image";
48 char * device_name = NULL;
49
usage(void)50 static void usage(void)
51 {
52 fprintf(stderr, _("Usage: %s [-rsI] device image_file\n"),
53 program_name);
54 exit (1);
55 }
56
write_header(int fd,struct ext2_image_hdr * hdr,int blocksize)57 static void write_header(int fd, struct ext2_image_hdr *hdr, int blocksize)
58 {
59 char *header_buf;
60 int actual;
61
62 header_buf = malloc(blocksize);
63 if (!header_buf) {
64 fputs(_("Couldn't allocate header buffer\n"), stderr);
65 exit(1);
66 }
67
68 if (lseek(fd, 0, SEEK_SET) < 0) {
69 perror("lseek while writing header");
70 exit(1);
71 }
72 memset(header_buf, 0, blocksize);
73
74 if (hdr)
75 memcpy(header_buf, hdr, sizeof(struct ext2_image_hdr));
76
77 actual = write(fd, header_buf, blocksize);
78 if (actual < 0) {
79 perror("write header");
80 exit(1);
81 }
82 if (actual != blocksize) {
83 fprintf(stderr, _("short write (only %d bytes) for "
84 "writing image header"), actual);
85 exit(1);
86 }
87 free(header_buf);
88 }
89
write_image_file(ext2_filsys fs,int fd)90 static void write_image_file(ext2_filsys fs, int fd)
91 {
92 struct ext2_image_hdr hdr;
93 struct stat st;
94 errcode_t retval;
95
96 write_header(fd, NULL, fs->blocksize);
97 memset(&hdr, 0, sizeof(struct ext2_image_hdr));
98
99 hdr.offset_super = lseek(fd, 0, SEEK_CUR);
100 retval = ext2fs_image_super_write(fs, fd, 0);
101 if (retval) {
102 com_err(program_name, retval, _("while writing superblock"));
103 exit(1);
104 }
105
106 hdr.offset_inode = lseek(fd, 0, SEEK_CUR);
107 retval = ext2fs_image_inode_write(fs, fd,
108 (fd != 1) ? IMAGER_FLAG_SPARSEWRITE : 0);
109 if (retval) {
110 com_err(program_name, retval, _("while writing inode table"));
111 exit(1);
112 }
113
114 hdr.offset_blockmap = lseek(fd, 0, SEEK_CUR);
115 retval = ext2fs_image_bitmap_write(fs, fd, 0);
116 if (retval) {
117 com_err(program_name, retval, _("while writing block bitmap"));
118 exit(1);
119 }
120
121 hdr.offset_inodemap = lseek(fd, 0, SEEK_CUR);
122 retval = ext2fs_image_bitmap_write(fs, fd, IMAGER_FLAG_INODEMAP);
123 if (retval) {
124 com_err(program_name, retval, _("while writing inode bitmap"));
125 exit(1);
126 }
127
128 hdr.magic_number = EXT2_ET_MAGIC_E2IMAGE;
129 strcpy(hdr.magic_descriptor, "Ext2 Image 1.0");
130 gethostname(hdr.fs_hostname, sizeof(hdr.fs_hostname));
131 strncpy(hdr.fs_device_name, device_name, sizeof(hdr.fs_device_name)-1);
132 hdr.fs_device_name[sizeof(hdr.fs_device_name) - 1] = 0;
133 hdr.fs_blocksize = fs->blocksize;
134
135 if (stat(device_name, &st) == 0)
136 hdr.fs_device = st.st_rdev;
137
138 if (fstat(fd, &st) == 0) {
139 hdr.image_device = st.st_dev;
140 hdr.image_inode = st.st_ino;
141 }
142 memcpy(hdr.fs_uuid, fs->super->s_uuid, sizeof(hdr.fs_uuid));
143
144 hdr.image_time = time(0);
145 write_header(fd, &hdr, fs->blocksize);
146 }
147
148 /*
149 * These set of functions are used to write a RAW image file.
150 */
151 ext2fs_block_bitmap meta_block_map;
152 ext2fs_block_bitmap scramble_block_map; /* Directory blocks to be scrambled */
153
154 struct process_block_struct {
155 ext2_ino_t ino;
156 int is_dir;
157 };
158
159 /*
160 * These subroutines short circuits ext2fs_get_blocks and
161 * ext2fs_check_directory; we use them since we already have the inode
162 * structure, so there's no point in letting the ext2fs library read
163 * the inode again.
164 */
165 static ino_t stashed_ino = 0;
166 static struct ext2_inode *stashed_inode;
167
meta_get_blocks(ext2_filsys fs EXT2FS_ATTR ((unused)),ext2_ino_t ino,blk_t * blocks)168 static errcode_t meta_get_blocks(ext2_filsys fs EXT2FS_ATTR((unused)),
169 ext2_ino_t ino,
170 blk_t *blocks)
171 {
172 int i;
173
174 if ((ino != stashed_ino) || !stashed_inode)
175 return EXT2_ET_CALLBACK_NOTHANDLED;
176
177 for (i=0; i < EXT2_N_BLOCKS; i++)
178 blocks[i] = stashed_inode->i_block[i];
179 return 0;
180 }
181
meta_check_directory(ext2_filsys fs EXT2FS_ATTR ((unused)),ext2_ino_t ino)182 static errcode_t meta_check_directory(ext2_filsys fs EXT2FS_ATTR((unused)),
183 ext2_ino_t ino)
184 {
185 if ((ino != stashed_ino) || !stashed_inode)
186 return EXT2_ET_CALLBACK_NOTHANDLED;
187
188 if (!LINUX_S_ISDIR(stashed_inode->i_mode))
189 return EXT2_ET_NO_DIRECTORY;
190 return 0;
191 }
192
meta_read_inode(ext2_filsys fs EXT2FS_ATTR ((unused)),ext2_ino_t ino,struct ext2_inode * inode)193 static errcode_t meta_read_inode(ext2_filsys fs EXT2FS_ATTR((unused)),
194 ext2_ino_t ino,
195 struct ext2_inode *inode)
196 {
197 if ((ino != stashed_ino) || !stashed_inode)
198 return EXT2_ET_CALLBACK_NOTHANDLED;
199 *inode = *stashed_inode;
200 return 0;
201 }
202
use_inode_shortcuts(ext2_filsys fs,int bool)203 static void use_inode_shortcuts(ext2_filsys fs, int bool)
204 {
205 if (bool) {
206 fs->get_blocks = meta_get_blocks;
207 fs->check_directory = meta_check_directory;
208 fs->read_inode = meta_read_inode;
209 stashed_ino = 0;
210 } else {
211 fs->get_blocks = 0;
212 fs->check_directory = 0;
213 fs->read_inode = 0;
214 }
215 }
216
process_dir_block(ext2_filsys fs EXT2FS_ATTR ((unused)),blk_t * block_nr,e2_blkcnt_t blockcnt EXT2FS_ATTR ((unused)),blk_t ref_block EXT2FS_ATTR ((unused)),int ref_offset EXT2FS_ATTR ((unused)),void * priv_data EXT2FS_ATTR ((unused)))217 static int process_dir_block(ext2_filsys fs EXT2FS_ATTR((unused)),
218 blk_t *block_nr,
219 e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)),
220 blk_t ref_block EXT2FS_ATTR((unused)),
221 int ref_offset EXT2FS_ATTR((unused)),
222 void *priv_data EXT2FS_ATTR((unused)))
223 {
224 struct process_block_struct *p;
225
226 p = (struct process_block_struct *) priv_data;
227
228 ext2fs_mark_block_bitmap(meta_block_map, *block_nr);
229 if (scramble_block_map && p->is_dir && blockcnt >= 0)
230 ext2fs_mark_block_bitmap(scramble_block_map, *block_nr);
231 return 0;
232 }
233
process_file_block(ext2_filsys fs EXT2FS_ATTR ((unused)),blk_t * block_nr,e2_blkcnt_t blockcnt,blk_t ref_block EXT2FS_ATTR ((unused)),int ref_offset EXT2FS_ATTR ((unused)),void * priv_data EXT2FS_ATTR ((unused)))234 static int process_file_block(ext2_filsys fs EXT2FS_ATTR((unused)),
235 blk_t *block_nr,
236 e2_blkcnt_t blockcnt,
237 blk_t ref_block EXT2FS_ATTR((unused)),
238 int ref_offset EXT2FS_ATTR((unused)),
239 void *priv_data EXT2FS_ATTR((unused)))
240 {
241 if (blockcnt < 0) {
242 ext2fs_mark_block_bitmap(meta_block_map, *block_nr);
243 }
244 return 0;
245 }
246
mark_table_blocks(ext2_filsys fs)247 static void mark_table_blocks(ext2_filsys fs)
248 {
249 blk_t first_block, b;
250 unsigned int i,j;
251
252 first_block = fs->super->s_first_data_block;
253 /*
254 * Mark primary superblock
255 */
256 ext2fs_mark_block_bitmap(meta_block_map, first_block);
257
258 /*
259 * Mark the primary superblock descriptors
260 */
261 for (j = 0; j < fs->desc_blocks; j++) {
262 ext2fs_mark_block_bitmap(meta_block_map,
263 ext2fs_descriptor_block_loc(fs, first_block, j));
264 }
265
266 for (i = 0; i < fs->group_desc_count; i++) {
267 /*
268 * Mark the blocks used for the inode table
269 */
270 if (fs->group_desc[i].bg_inode_table) {
271 for (j = 0, b = fs->group_desc[i].bg_inode_table;
272 j < (unsigned) fs->inode_blocks_per_group;
273 j++, b++)
274 ext2fs_mark_block_bitmap(meta_block_map, b);
275 }
276
277 /*
278 * Mark block used for the block bitmap
279 */
280 if (fs->group_desc[i].bg_block_bitmap) {
281 ext2fs_mark_block_bitmap(meta_block_map,
282 fs->group_desc[i].bg_block_bitmap);
283 }
284
285 /*
286 * Mark block used for the inode bitmap
287 */
288 if (fs->group_desc[i].bg_inode_bitmap) {
289 ext2fs_mark_block_bitmap(meta_block_map,
290 fs->group_desc[i].bg_inode_bitmap);
291 }
292 }
293 }
294
295 /*
296 * This function returns 1 if the specified block is all zeros
297 */
check_zero_block(char * buf,int blocksize)298 static int check_zero_block(char *buf, int blocksize)
299 {
300 char *cp = buf;
301 int left = blocksize;
302
303 while (left > 0) {
304 if (*cp++)
305 return 0;
306 left--;
307 }
308 return 1;
309 }
310
write_block(int fd,char * buf,int sparse_offset,int blocksize,blk_t block)311 static void write_block(int fd, char *buf, int sparse_offset,
312 int blocksize, blk_t block)
313 {
314 int count;
315 errcode_t err;
316
317 if (sparse_offset) {
318 #ifdef HAVE_LSEEK64
319 if (lseek64(fd, sparse_offset, SEEK_CUR) < 0)
320 perror("lseek");
321 #else
322 if (lseek(fd, sparse_offset, SEEK_CUR) < 0)
323 perror("lseek");
324 #endif
325 }
326 if (blocksize) {
327 count = write(fd, buf, blocksize);
328 if (count != blocksize) {
329 if (count == -1)
330 err = errno;
331 else
332 err = 0;
333 com_err(program_name, err, "error writing block %u",
334 block);
335 exit(1);
336 }
337 }
338 }
339
340 int name_id[256];
341
342 #define EXT4_MAX_REC_LEN ((1<<16)-1)
343
scramble_dir_block(ext2_filsys fs,blk_t blk,char * buf)344 static void scramble_dir_block(ext2_filsys fs, blk_t blk, char *buf)
345 {
346 char *p, *end, *cp;
347 struct ext2_dir_entry_2 *dirent;
348 unsigned int rec_len;
349 int id, len;
350
351 end = buf + fs->blocksize;
352 for (p = buf; p < end-8; p += rec_len) {
353 dirent = (struct ext2_dir_entry_2 *) p;
354 rec_len = dirent->rec_len;
355 #ifdef WORDS_BIGENDIAN
356 rec_len = ext2fs_swab16(rec_len);
357 #endif
358 if (rec_len == EXT4_MAX_REC_LEN || rec_len == 0)
359 rec_len = fs->blocksize;
360 else
361 rec_len = (rec_len & 65532) | ((rec_len & 3) << 16);
362 #if 0
363 printf("rec_len = %d, name_len = %d\n", rec_len, dirent->name_len);
364 #endif
365 if (rec_len < 8 || (rec_len % 4) ||
366 (p+rec_len > end)) {
367 printf("Corrupt directory block %lu: "
368 "bad rec_len (%d)\n", (unsigned long) blk,
369 rec_len);
370 rec_len = end - p;
371 (void) ext2fs_set_rec_len(fs, rec_len,
372 (struct ext2_dir_entry *) dirent);
373 #ifdef WORDS_BIGENDIAN
374 dirent->rec_len = ext2fs_swab16(dirent->rec_len);
375 #endif
376 continue;
377 }
378 if (dirent->name_len + 8 > rec_len) {
379 printf("Corrupt directory block %lu: "
380 "bad name_len (%d)\n", (unsigned long) blk,
381 dirent->name_len);
382 dirent->name_len = rec_len - 8;
383 continue;
384 }
385 cp = p+8;
386 len = rec_len - dirent->name_len - 8;
387 if (len > 0)
388 memset(cp+dirent->name_len, 0, len);
389 if (dirent->name_len==1 && cp[0] == '.')
390 continue;
391 if (dirent->name_len==2 && cp[0] == '.' && cp[1] == '.')
392 continue;
393
394 memset(cp, 'A', dirent->name_len);
395 len = dirent->name_len;
396 id = name_id[len]++;
397 while ((len > 0) && (id > 0)) {
398 *cp += id % 26;
399 id = id / 26;
400 cp++;
401 len--;
402 }
403 }
404 }
405
output_meta_data_blocks(ext2_filsys fs,int fd)406 static void output_meta_data_blocks(ext2_filsys fs, int fd)
407 {
408 errcode_t retval;
409 blk_t blk;
410 char *buf, *zero_buf;
411 int sparse = 0;
412
413 buf = malloc(fs->blocksize);
414 if (!buf) {
415 com_err(program_name, ENOMEM, "while allocating buffer");
416 exit(1);
417 }
418 zero_buf = malloc(fs->blocksize);
419 if (!zero_buf) {
420 com_err(program_name, ENOMEM, "while allocating buffer");
421 exit(1);
422 }
423 memset(zero_buf, 0, fs->blocksize);
424 for (blk = 0; blk < fs->super->s_blocks_count; blk++) {
425 if ((blk >= fs->super->s_first_data_block) &&
426 ext2fs_test_block_bitmap(meta_block_map, blk)) {
427 retval = io_channel_read_blk(fs->io, blk, 1, buf);
428 if (retval) {
429 com_err(program_name, retval,
430 "error reading block %u", blk);
431 }
432 if (scramble_block_map &&
433 ext2fs_test_block_bitmap(scramble_block_map, blk))
434 scramble_dir_block(fs, blk, buf);
435 if ((fd != 1) && check_zero_block(buf, fs->blocksize))
436 goto sparse_write;
437 write_block(fd, buf, sparse, fs->blocksize, blk);
438 sparse = 0;
439 } else {
440 sparse_write:
441 if (fd == 1) {
442 write_block(fd, zero_buf, 0,
443 fs->blocksize, blk);
444 continue;
445 }
446 sparse += fs->blocksize;
447 if (sparse >= 1024*1024) {
448 write_block(fd, 0, sparse, 0, 0);
449 sparse = 0;
450 }
451 }
452 }
453 if (sparse)
454 write_block(fd, zero_buf, sparse-1, 1, -1);
455 free(zero_buf);
456 free(buf);
457 }
458
write_raw_image_file(ext2_filsys fs,int fd,int scramble_flag)459 static void write_raw_image_file(ext2_filsys fs, int fd, int scramble_flag)
460 {
461 struct process_block_struct pb;
462 struct ext2_inode inode;
463 ext2_inode_scan scan;
464 ext2_ino_t ino;
465 errcode_t retval;
466 char * block_buf;
467
468 retval = ext2fs_allocate_block_bitmap(fs, "in-use block map",
469 &meta_block_map);
470 if (retval) {
471 com_err(program_name, retval, "while allocating block bitmap");
472 exit(1);
473 }
474
475 if (scramble_flag) {
476 retval = ext2fs_allocate_block_bitmap(fs, "scramble block map",
477 &scramble_block_map);
478 if (retval) {
479 com_err(program_name, retval,
480 "while allocating scramble block bitmap");
481 exit(1);
482 }
483 }
484
485 mark_table_blocks(fs);
486
487 retval = ext2fs_open_inode_scan(fs, 0, &scan);
488 if (retval) {
489 com_err(program_name, retval, _("while opening inode scan"));
490 exit(1);
491 }
492
493 block_buf = malloc(fs->blocksize * 3);
494 if (!block_buf) {
495 com_err(program_name, 0, "Can't allocate block buffer");
496 exit(1);
497 }
498
499 use_inode_shortcuts(fs, 1);
500 stashed_inode = &inode;
501 while (1) {
502 retval = ext2fs_get_next_inode(scan, &ino, &inode);
503 if (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE)
504 continue;
505 if (retval) {
506 com_err(program_name, retval,
507 _("while getting next inode"));
508 exit(1);
509 }
510 if (ino == 0)
511 break;
512 if (!inode.i_links_count)
513 continue;
514 if (inode.i_file_acl) {
515 ext2fs_mark_block_bitmap(meta_block_map,
516 inode.i_file_acl);
517 }
518 if (!ext2fs_inode_has_valid_blocks(&inode))
519 continue;
520
521 stashed_ino = ino;
522 pb.ino = ino;
523 pb.is_dir = LINUX_S_ISDIR(inode.i_mode);
524 if (LINUX_S_ISDIR(inode.i_mode) ||
525 (LINUX_S_ISLNK(inode.i_mode) &&
526 ext2fs_inode_has_valid_blocks(&inode)) ||
527 ino == fs->super->s_journal_inum) {
528 retval = ext2fs_block_iterate2(fs, ino,
529 BLOCK_FLAG_READ_ONLY, block_buf,
530 process_dir_block, &pb);
531 if (retval) {
532 com_err(program_name, retval,
533 "while iterating over inode %u",
534 ino);
535 exit(1);
536 }
537 } else {
538 if ((inode.i_flags & EXT4_EXTENTS_FL) ||
539 inode.i_block[EXT2_IND_BLOCK] ||
540 inode.i_block[EXT2_DIND_BLOCK] ||
541 inode.i_block[EXT2_TIND_BLOCK]) {
542 retval = ext2fs_block_iterate2(fs,
543 ino, BLOCK_FLAG_READ_ONLY, block_buf,
544 process_file_block, &pb);
545 if (retval) {
546 com_err(program_name, retval,
547 "while iterating over inode %u", ino);
548 exit(1);
549 }
550 }
551 }
552 }
553 use_inode_shortcuts(fs, 0);
554 output_meta_data_blocks(fs, fd);
555 free(block_buf);
556 }
557
install_image(char * device,char * image_fn,int raw_flag)558 static void install_image(char *device, char *image_fn, int raw_flag)
559 {
560 errcode_t retval;
561 ext2_filsys fs;
562 int open_flag = EXT2_FLAG_IMAGE_FILE;
563 int fd = 0;
564 io_manager io_ptr;
565 io_channel io, image_io;
566
567 if (raw_flag) {
568 com_err(program_name, 0, "Raw images cannot be installed");
569 exit(1);
570 }
571
572 #ifdef CONFIG_TESTIO_DEBUG
573 if (getenv("TEST_IO_FLAGS") || getenv("TEST_IO_BLOCK")) {
574 io_ptr = test_io_manager;
575 test_io_backing_manager = unix_io_manager;
576 } else
577 #endif
578 io_ptr = unix_io_manager;
579
580 retval = ext2fs_open (image_fn, open_flag, 0, 0,
581 io_ptr, &fs);
582 if (retval) {
583 com_err (program_name, retval, _("while trying to open %s"),
584 image_fn);
585 exit(1);
586 }
587
588 retval = ext2fs_read_bitmaps (fs);
589 if (retval) {
590 com_err(program_name, retval, "error reading bitmaps");
591 exit(1);
592 }
593
594 #ifdef HAVE_OPEN64
595 fd = open64(image_fn, O_RDONLY);
596 #else
597 fd = open(image_fn, O_RDONLY);
598 #endif
599 if (fd < 0) {
600 perror(image_fn);
601 exit(1);
602 }
603
604 retval = io_ptr->open(device, IO_FLAG_RW, &io);
605 if (retval) {
606 com_err(device, 0, "while opening device file");
607 exit(1);
608 }
609
610 image_io = fs->io;
611
612 ext2fs_rewrite_to_io(fs, io);
613
614 if (lseek(fd, fs->image_header->offset_inode, SEEK_SET) < 0) {
615 perror("lseek");
616 exit(1);
617 }
618
619 retval = ext2fs_image_inode_read(fs, fd, 0);
620 if (retval) {
621 com_err(image_fn, 0, "while restoring the image table");
622 exit(1);
623 }
624
625 ext2fs_close (fs);
626 exit (0);
627 }
628
main(int argc,char ** argv)629 int main (int argc, char ** argv)
630 {
631 int c;
632 errcode_t retval;
633 ext2_filsys fs;
634 char *image_fn;
635 int open_flag = 0;
636 int raw_flag = 0;
637 int install_flag = 0;
638 int scramble_flag = 0;
639 int fd = 0;
640
641 #ifdef ENABLE_NLS
642 setlocale(LC_MESSAGES, "");
643 setlocale(LC_CTYPE, "");
644 bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
645 textdomain(NLS_CAT_NAME);
646 #endif
647 fprintf (stderr, "e2image %s (%s)\n", E2FSPROGS_VERSION,
648 E2FSPROGS_DATE);
649 if (argc && *argv)
650 program_name = *argv;
651 add_error_table(&et_ext2_error_table);
652 while ((c = getopt (argc, argv, "rsI")) != EOF)
653 switch (c) {
654 case 'r':
655 raw_flag++;
656 break;
657 case 's':
658 scramble_flag++;
659 break;
660 case 'I':
661 install_flag++;
662 break;
663 default:
664 usage();
665 }
666 if (optind != argc - 2 )
667 usage();
668 device_name = argv[optind];
669 image_fn = argv[optind+1];
670
671 if (install_flag) {
672 install_image(device_name, image_fn, raw_flag);
673 exit (0);
674 }
675
676 retval = ext2fs_open (device_name, open_flag, 0, 0,
677 unix_io_manager, &fs);
678 if (retval) {
679 com_err (program_name, retval, _("while trying to open %s"),
680 device_name);
681 fputs(_("Couldn't find valid filesystem superblock.\n"), stdout);
682 exit(1);
683 }
684
685 if (strcmp(image_fn, "-") == 0)
686 fd = 1;
687 else {
688 #ifdef HAVE_OPEN64
689 fd = open64(image_fn, O_CREAT|O_TRUNC|O_WRONLY, 0600);
690 #else
691 fd = open(image_fn, O_CREAT|O_TRUNC|O_WRONLY, 0600);
692 #endif
693 if (fd < 0) {
694 com_err(program_name, errno,
695 _("while trying to open %s"), argv[optind+1]);
696 exit(1);
697 }
698 }
699
700 if (raw_flag)
701 write_raw_image_file(fs, fd, scramble_flag);
702 else
703 write_image_file(fs, fd);
704
705 ext2fs_close (fs);
706 remove_error_table(&et_ext2_error_table);
707 exit (0);
708 }
709