1 /* 2 * logdump.c --- dump the contents of the journal out to a file 3 * 4 * Author: Stephen C. Tweedie, 2001 <sct (at) redhat.com> 5 * Copyright (C) 2001 Red Hat, Inc. 6 * Based on portions Copyright (C) 1994 Theodore Ts'o. 7 * 8 * This file may be redistributed under the terms of the GNU Public 9 * License. 10 */ 11 12 #include "config.h" 13 #include <stdio.h> 14 #include <unistd.h> 15 #include <stdlib.h> 16 #include <ctype.h> 17 #include <string.h> 18 #include <time.h> 19 #ifdef HAVE_ERRNO_H 20 #include <errno.h> 21 #endif 22 #include <sys/types.h> 23 #include <sys/stat.h> 24 #include <fcntl.h> 25 #include <utime.h> 26 #ifdef HAVE_GETOPT_H 27 #include <getopt.h> 28 #else 29 extern int optind; 30 extern char *optarg; 31 #endif 32 33 #include "debugfs.h" 34 #include "blkid/blkid.h" 35 #include "jfs_user.h" 36 #include <uuid/uuid.h> 37 38 enum journal_location {JOURNAL_IS_INTERNAL, JOURNAL_IS_EXTERNAL}; 39 40 #define ANY_BLOCK ((blk64_t) -1) 41 42 static int dump_all, dump_super, dump_old, dump_contents, dump_descriptors; 43 static blk64_t block_to_dump, bitmap_to_dump, inode_block_to_dump; 44 static unsigned int group_to_dump, inode_offset_to_dump; 45 static ext2_ino_t inode_to_dump; 46 47 struct journal_source 48 { 49 enum journal_location where; 50 int fd; 51 ext2_file_t file; 52 }; 53 54 static void dump_journal(char *, FILE *, struct journal_source *); 55 56 static void dump_descriptor_block(FILE *, struct journal_source *, 57 char *, journal_superblock_t *, 58 unsigned int *, int, tid_t); 59 60 static void dump_revoke_block(FILE *, char *, journal_superblock_t *, 61 unsigned int, int, tid_t); 62 63 static void dump_metadata_block(FILE *, struct journal_source *, 64 journal_superblock_t*, 65 unsigned int, unsigned int, unsigned int, 66 int, tid_t); 67 68 static void do_hexdump (FILE *, char *, int); 69 70 #define WRAP(jsb, blocknr) \ 71 if (blocknr >= be32_to_cpu((jsb)->s_maxlen)) \ 72 blocknr -= (be32_to_cpu((jsb)->s_maxlen) - \ 73 be32_to_cpu((jsb)->s_first)); 74 75 void do_logdump(int argc, char **argv, int sci_idx EXT2FS_ATTR((unused)), 76 void *infop EXT2FS_ATTR((unused))) 77 { 78 int c; 79 int retval; 80 char *out_fn; 81 FILE *out_file; 82 83 char *inode_spec = NULL; 84 char *journal_fn = NULL; 85 int journal_fd = 0; 86 int use_sb = 0; 87 ext2_ino_t journal_inum; 88 struct ext2_inode journal_inode; 89 ext2_file_t journal_file; 90 char *tmp; 91 struct journal_source journal_source; 92 struct ext2_super_block *es = NULL; 93 94 journal_source.where = JOURNAL_IS_INTERNAL; 95 journal_source.fd = 0; 96 journal_source.file = 0; 97 dump_all = 0; 98 dump_old = 0; 99 dump_contents = 0; 100 dump_super = 0; 101 dump_descriptors = 1; 102 block_to_dump = ANY_BLOCK; 103 bitmap_to_dump = -1; 104 inode_block_to_dump = ANY_BLOCK; 105 inode_to_dump = -1; 106 107 reset_getopt(); 108 while ((c = getopt (argc, argv, "ab:ci:f:OsS")) != EOF) { 109 switch (c) { 110 case 'a': 111 dump_all++; 112 break; 113 case 'b': 114 block_to_dump = strtoul(optarg, &tmp, 0); 115 if (*tmp) { 116 com_err(argv[0], 0, 117 "Bad block number - %s", optarg); 118 return; 119 } 120 dump_descriptors = 0; 121 break; 122 case 'c': 123 dump_contents++; 124 break; 125 case 'f': 126 journal_fn = optarg; 127 break; 128 case 'i': 129 inode_spec = optarg; 130 dump_descriptors = 0; 131 break; 132 case 'O': 133 dump_old++; 134 break; 135 case 's': 136 use_sb++; 137 break; 138 case 'S': 139 dump_super++; 140 break; 141 default: 142 goto print_usage; 143 } 144 } 145 if (optind != argc && optind != argc-1) { 146 goto print_usage; 147 } 148 149 if (current_fs) 150 es = current_fs->super; 151 152 if (inode_spec) { 153 int inode_group, group_offset, inodes_per_block; 154 155 if (check_fs_open(argv[0])) 156 return; 157 158 inode_to_dump = string_to_inode(inode_spec); 159 if (!inode_to_dump) 160 return; 161 162 inode_group = ((inode_to_dump - 1) 163 / es->s_inodes_per_group); 164 group_offset = ((inode_to_dump - 1) 165 % es->s_inodes_per_group); 166 inodes_per_block = (current_fs->blocksize 167 / sizeof(struct ext2_inode)); 168 169 inode_block_to_dump = 170 ext2fs_inode_table_loc(current_fs, inode_group) + 171 (group_offset / inodes_per_block); 172 inode_offset_to_dump = ((group_offset % inodes_per_block) 173 * sizeof(struct ext2_inode)); 174 printf("Inode %u is at group %u, block %llu, offset %u\n", 175 inode_to_dump, inode_group, 176 inode_block_to_dump, inode_offset_to_dump); 177 } 178 179 if (optind == argc) { 180 out_file = stdout; 181 } else { 182 out_fn = argv[optind]; 183 out_file = fopen(out_fn, "w"); 184 if (!out_file) { 185 com_err(argv[0], errno, "while opening %s for logdump", 186 out_fn); 187 goto errout; 188 } 189 } 190 191 if (block_to_dump != ANY_BLOCK && current_fs != NULL) { 192 group_to_dump = ((block_to_dump - 193 es->s_first_data_block) 194 / es->s_blocks_per_group); 195 bitmap_to_dump = ext2fs_block_bitmap_loc(current_fs, group_to_dump); 196 } 197 198 if (!journal_fn && check_fs_open(argv[0])) 199 goto errout; 200 201 if (journal_fn) { 202 /* Set up to read journal from a regular file somewhere */ 203 journal_fd = open(journal_fn, O_RDONLY, 0); 204 if (journal_fd < 0) { 205 com_err(argv[0], errno, "while opening %s for logdump", 206 journal_fn); 207 goto errout; 208 } 209 210 journal_source.where = JOURNAL_IS_EXTERNAL; 211 journal_source.fd = journal_fd; 212 } else if ((journal_inum = es->s_journal_inum)) { 213 if (use_sb) { 214 if (es->s_jnl_backup_type != EXT3_JNL_BACKUP_BLOCKS) { 215 com_err(argv[0], 0, 216 "no journal backup in super block\n"); 217 goto errout; 218 } 219 memset(&journal_inode, 0, sizeof(struct ext2_inode)); 220 memcpy(&journal_inode.i_block[0], es->s_jnl_blocks, 221 EXT2_N_BLOCKS*4); 222 journal_inode.i_size_high = es->s_jnl_blocks[15]; 223 journal_inode.i_size = es->s_jnl_blocks[16]; 224 journal_inode.i_links_count = 1; 225 journal_inode.i_mode = LINUX_S_IFREG | 0600; 226 } else { 227 if (debugfs_read_inode(journal_inum, &journal_inode, 228 argv[0])) 229 goto errout; 230 } 231 232 retval = ext2fs_file_open2(current_fs, journal_inum, 233 &journal_inode, 0, &journal_file); 234 if (retval) { 235 com_err(argv[0], retval, "while opening ext2 file"); 236 goto errout; 237 } 238 journal_source.where = JOURNAL_IS_INTERNAL; 239 journal_source.file = journal_file; 240 } else { 241 char uuid[37]; 242 243 uuid_unparse(es->s_journal_uuid, uuid); 244 journal_fn = blkid_get_devname(NULL, "UUID", uuid); 245 if (!journal_fn) 246 journal_fn = blkid_devno_to_devname(es->s_journal_dev); 247 if (!journal_fn) { 248 com_err(argv[0], 0, "filesystem has no journal"); 249 goto errout; 250 } 251 journal_fd = open(journal_fn, O_RDONLY, 0); 252 if (journal_fd < 0) { 253 com_err(argv[0], errno, "while opening %s for logdump", 254 journal_fn); 255 free(journal_fn); 256 goto errout; 257 } 258 fprintf(out_file, "Using external journal found at %s\n", 259 journal_fn); 260 free(journal_fn); 261 journal_source.where = JOURNAL_IS_EXTERNAL; 262 journal_source.fd = journal_fd; 263 } 264 265 dump_journal(argv[0], out_file, &journal_source); 266 267 if (journal_source.where == JOURNAL_IS_INTERNAL) 268 ext2fs_file_close(journal_file); 269 else 270 close(journal_fd); 271 272 errout: 273 if (out_file && (out_file != stdout)) 274 fclose(out_file); 275 276 return; 277 278 print_usage: 279 fprintf(stderr, "%s: Usage: logdump [-acsOS] [-b<block>] [-i<filespec>]\n\t" 280 "[-f<journal_file>] [output_file]\n", argv[0]); 281 } 282 283 284 static int read_journal_block(const char *cmd, struct journal_source *source, 285 ext2_loff_t offset, char *buf, unsigned int size) 286 { 287 int retval; 288 unsigned int got; 289 290 if (source->where == JOURNAL_IS_EXTERNAL) { 291 if (lseek(source->fd, offset, SEEK_SET) < 0) { 292 retval = errno; 293 goto seek_err; 294 } 295 retval = read(source->fd, buf, size); 296 if (retval < 0) { 297 retval = errno; 298 goto read_err; 299 } 300 got = retval; 301 retval = 0; 302 } else { 303 retval = ext2fs_file_llseek(source->file, offset, 304 EXT2_SEEK_SET, NULL); 305 if (retval) { 306 seek_err: 307 com_err(cmd, retval, "while seeking in reading journal"); 308 return retval; 309 } 310 retval = ext2fs_file_read(source->file, buf, size, &got); 311 if (retval) { 312 read_err: 313 com_err(cmd, retval, "while reading journal"); 314 return retval; 315 } 316 } 317 if (got != size) { 318 com_err(cmd, 0, "short read (read %u, expected %u) " 319 "while reading journal", got, size); 320 retval = -1; 321 } 322 return retval; 323 } 324 325 static const char *type_to_name(int btype) 326 { 327 switch (btype) { 328 case JFS_DESCRIPTOR_BLOCK: 329 return "descriptor block"; 330 case JFS_COMMIT_BLOCK: 331 return "commit block"; 332 case JFS_SUPERBLOCK_V1: 333 return "V1 superblock"; 334 case JFS_SUPERBLOCK_V2: 335 return "V2 superblock"; 336 case JFS_REVOKE_BLOCK: 337 return "revoke table"; 338 } 339 return "unrecognised type"; 340 } 341 342 343 static void dump_journal(char *cmdname, FILE *out_file, 344 struct journal_source *source) 345 { 346 struct ext2_super_block *sb; 347 char jsb_buffer[1024]; 348 char buf[8192]; 349 journal_superblock_t *jsb; 350 unsigned int blocksize = 1024; 351 int retval; 352 __u32 magic, sequence, blocktype; 353 journal_header_t *header; 354 tid_t transaction; 355 unsigned int blocknr = 0; 356 357 /* First, check to see if there's an ext2 superblock header */ 358 retval = read_journal_block(cmdname, source, 0, buf, 2048); 359 if (retval) 360 return; 361 362 jsb = (journal_superblock_t *) buf; 363 sb = (struct ext2_super_block *) (buf+1024); 364 #ifdef WORDS_BIGENDIAN 365 if (sb->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC)) 366 ext2fs_swap_super(sb); 367 #endif 368 369 if ((be32_to_cpu(jsb->s_header.h_magic) != JFS_MAGIC_NUMBER) && 370 (sb->s_magic == EXT2_SUPER_MAGIC) && 371 ext2fs_has_feature_journal_dev(sb)) { 372 blocksize = EXT2_BLOCK_SIZE(sb); 373 blocknr = (blocksize == 1024) ? 2 : 1; 374 uuid_unparse(sb->s_uuid, jsb_buffer); 375 fprintf(out_file, "Ext2 superblock header found.\n"); 376 if (dump_all) { 377 fprintf(out_file, "\tuuid=%s\n", jsb_buffer); 378 fprintf(out_file, "\tblocksize=%d\n", blocksize); 379 fprintf(out_file, "\tjournal data size %lu\n", 380 (unsigned long) ext2fs_blocks_count(sb)); 381 } 382 } 383 384 /* Next, read the journal superblock */ 385 retval = read_journal_block(cmdname, source, 386 ((ext2_loff_t) blocknr) * blocksize, 387 jsb_buffer, 1024); 388 if (retval) 389 return; 390 391 if (dump_super) { 392 e2p_list_journal_super(out_file, jsb_buffer, 393 current_fs->blocksize, 0); 394 fputc('\n', out_file); 395 } 396 397 jsb = (journal_superblock_t *) jsb_buffer; 398 if (be32_to_cpu(jsb->s_header.h_magic) != JFS_MAGIC_NUMBER) { 399 fprintf(out_file, 400 "Journal superblock magic number invalid!\n"); 401 return; 402 } 403 blocksize = be32_to_cpu(jsb->s_blocksize); 404 transaction = be32_to_cpu(jsb->s_sequence); 405 blocknr = be32_to_cpu(jsb->s_start); 406 407 fprintf(out_file, "Journal starts at block %u, transaction %u\n", 408 blocknr, transaction); 409 410 if (!blocknr) { 411 /* Empty journal, nothing to do. */ 412 if (!dump_old) 413 return; 414 else 415 blocknr = 1; 416 } 417 418 while (1) { 419 retval = read_journal_block(cmdname, source, 420 ((ext2_loff_t) blocknr) * blocksize, 421 buf, blocksize); 422 if (retval) 423 return; 424 425 header = (journal_header_t *) buf; 426 427 magic = be32_to_cpu(header->h_magic); 428 sequence = be32_to_cpu(header->h_sequence); 429 blocktype = be32_to_cpu(header->h_blocktype); 430 431 if (magic != JFS_MAGIC_NUMBER) { 432 fprintf (out_file, "No magic number at block %u: " 433 "end of journal.\n", blocknr); 434 return; 435 } 436 437 if (sequence != transaction) { 438 fprintf (out_file, "Found sequence %u (not %u) at " 439 "block %u: end of journal.\n", 440 sequence, transaction, blocknr); 441 if (!dump_old) 442 return; 443 } 444 445 if (dump_descriptors) { 446 fprintf (out_file, "Found expected sequence %u, " 447 "type %u (%s) at block %u\n", 448 sequence, blocktype, 449 type_to_name(blocktype), blocknr); 450 } 451 452 switch (blocktype) { 453 case JFS_DESCRIPTOR_BLOCK: 454 dump_descriptor_block(out_file, source, buf, jsb, 455 &blocknr, blocksize, 456 transaction); 457 continue; 458 459 case JFS_COMMIT_BLOCK: 460 transaction++; 461 blocknr++; 462 WRAP(jsb, blocknr); 463 continue; 464 465 case JFS_REVOKE_BLOCK: 466 dump_revoke_block(out_file, buf, jsb, 467 blocknr, blocksize, 468 transaction); 469 blocknr++; 470 WRAP(jsb, blocknr); 471 continue; 472 473 default: 474 fprintf (out_file, "Unexpected block type %u at " 475 "block %u.\n", blocktype, blocknr); 476 return; 477 } 478 } 479 } 480 481 static inline size_t journal_super_tag_bytes(journal_superblock_t *jsb) 482 { 483 size_t sz; 484 485 if (JSB_HAS_INCOMPAT_FEATURE(jsb, JFS_FEATURE_INCOMPAT_CSUM_V3)) 486 return sizeof(journal_block_tag3_t); 487 488 sz = sizeof(journal_block_tag_t); 489 490 if (JSB_HAS_INCOMPAT_FEATURE(jsb, JFS_FEATURE_INCOMPAT_CSUM_V2)) 491 sz += sizeof(__u16); 492 493 if (JSB_HAS_INCOMPAT_FEATURE(jsb, JFS_FEATURE_INCOMPAT_64BIT)) 494 return sz; 495 496 return sz - sizeof(__u32); 497 } 498 499 static void dump_descriptor_block(FILE *out_file, 500 struct journal_source *source, 501 char *buf, 502 journal_superblock_t *jsb, 503 unsigned int *blockp, int blocksize, 504 tid_t transaction) 505 { 506 int offset, tag_size, csum_size = 0; 507 char *tagp; 508 journal_block_tag_t *tag; 509 unsigned int blocknr; 510 __u32 tag_block; 511 __u32 tag_flags; 512 513 tag_size = journal_super_tag_bytes(jsb); 514 offset = sizeof(journal_header_t); 515 blocknr = *blockp; 516 517 if (JSB_HAS_INCOMPAT_FEATURE(jsb, JFS_FEATURE_INCOMPAT_CSUM_V3) || 518 JSB_HAS_INCOMPAT_FEATURE(jsb, JFS_FEATURE_INCOMPAT_CSUM_V2)) 519 csum_size = sizeof(struct journal_block_tail); 520 521 if (dump_all) 522 fprintf(out_file, "Dumping descriptor block, sequence %u, at " 523 "block %u:\n", transaction, blocknr); 524 525 ++blocknr; 526 WRAP(jsb, blocknr); 527 528 do { 529 /* Work out the location of the current tag, and skip to 530 * the next one... */ 531 tagp = &buf[offset]; 532 tag = (journal_block_tag_t *) tagp; 533 offset += tag_size; 534 535 /* ... and if we have gone too far, then we've reached the 536 end of this block. */ 537 if (offset > blocksize - csum_size) 538 break; 539 540 tag_block = be32_to_cpu(tag->t_blocknr); 541 tag_flags = be16_to_cpu(tag->t_flags); 542 543 if (!(tag_flags & JFS_FLAG_SAME_UUID)) 544 offset += 16; 545 546 dump_metadata_block(out_file, source, jsb, 547 blocknr, tag_block, tag_flags, blocksize, 548 transaction); 549 550 ++blocknr; 551 WRAP(jsb, blocknr); 552 553 } while (!(tag_flags & JFS_FLAG_LAST_TAG)); 554 555 *blockp = blocknr; 556 } 557 558 559 static void dump_revoke_block(FILE *out_file, char *buf, 560 journal_superblock_t *jsb EXT2FS_ATTR((unused)), 561 unsigned int blocknr, 562 int blocksize EXT2FS_ATTR((unused)), 563 tid_t transaction) 564 { 565 int offset, max; 566 journal_revoke_header_t *header; 567 unsigned long long rblock; 568 int tag_size = sizeof(__u32); 569 570 if (dump_all) 571 fprintf(out_file, "Dumping revoke block, sequence %u, at " 572 "block %u:\n", transaction, blocknr); 573 574 if (be32_to_cpu(jsb->s_feature_incompat) & JFS_FEATURE_INCOMPAT_64BIT) 575 tag_size = sizeof(__u64); 576 577 header = (journal_revoke_header_t *) buf; 578 offset = sizeof(journal_revoke_header_t); 579 max = be32_to_cpu(header->r_count); 580 581 while (offset < max) { 582 if (tag_size == sizeof(__u32)) { 583 __u32 *entry = (__u32 *) (buf + offset); 584 rblock = be32_to_cpu(*entry); 585 } else { 586 __u64 *entry = (__u64 *) (buf + offset); 587 rblock = ext2fs_be64_to_cpu(*entry); 588 } 589 if (dump_all || rblock == block_to_dump) { 590 fprintf(out_file, " Revoke FS block %llu", rblock); 591 if (dump_all) 592 fprintf(out_file, "\n"); 593 else 594 fprintf(out_file," at block %u, sequence %u\n", 595 blocknr, transaction); 596 } 597 offset += tag_size; 598 } 599 } 600 601 602 static void show_extent(FILE *out_file, int start_extent, int end_extent, 603 __u32 first_block) 604 { 605 if (start_extent >= 0 && first_block != 0) 606 fprintf(out_file, "(%d+%u): %u ", 607 start_extent, end_extent-start_extent, first_block); 608 } 609 610 static void show_indirect(FILE *out_file, const char *name, __u32 where) 611 { 612 if (where) 613 fprintf(out_file, "(%s): %u ", name, where); 614 } 615 616 617 static void dump_metadata_block(FILE *out_file, struct journal_source *source, 618 journal_superblock_t *jsb EXT2FS_ATTR((unused)), 619 unsigned int log_blocknr, 620 unsigned int fs_blocknr, 621 unsigned int log_tag_flags, 622 int blocksize, 623 tid_t transaction) 624 { 625 int retval; 626 char buf[8192]; 627 628 if (!(dump_all 629 || (fs_blocknr == block_to_dump) 630 || (fs_blocknr == inode_block_to_dump) 631 || (fs_blocknr == bitmap_to_dump))) 632 return; 633 634 fprintf(out_file, " FS block %u logged at ", fs_blocknr); 635 if (!dump_all) 636 fprintf(out_file, "sequence %u, ", transaction); 637 fprintf(out_file, "journal block %u (flags 0x%x)\n", log_blocknr, 638 log_tag_flags); 639 640 /* There are two major special cases to parse: 641 * 642 * If this block is a block 643 * bitmap block, we need to give it special treatment so that we 644 * can log any allocates and deallocates which affect the 645 * block_to_dump query block. 646 * 647 * If the block is an inode block for the inode being searched 648 * for, then we need to dump the contents of that inode 649 * structure symbolically. 650 */ 651 652 if (!(dump_contents && dump_all) 653 && fs_blocknr != block_to_dump 654 && fs_blocknr != bitmap_to_dump 655 && fs_blocknr != inode_block_to_dump) 656 return; 657 658 retval = read_journal_block("logdump", source, 659 ((ext2_loff_t) log_blocknr) * blocksize, 660 buf, blocksize); 661 if (retval) 662 return; 663 664 if (fs_blocknr == bitmap_to_dump) { 665 struct ext2_super_block *super; 666 int offset; 667 668 super = current_fs->super; 669 offset = ((block_to_dump - super->s_first_data_block) % 670 super->s_blocks_per_group); 671 672 fprintf(out_file, " (block bitmap for block %llu: " 673 "block is %s)\n", 674 block_to_dump, 675 ext2fs_test_bit(offset, buf) ? "SET" : "CLEAR"); 676 } 677 678 if (fs_blocknr == inode_block_to_dump) { 679 struct ext2_inode *inode; 680 int first, prev, this, start_extent, i; 681 682 fprintf(out_file, " (inode block for inode %u):\n", 683 inode_to_dump); 684 685 inode = (struct ext2_inode *) (buf + inode_offset_to_dump); 686 internal_dump_inode(out_file, " ", inode_to_dump, inode, 0); 687 688 /* Dump out the direct/indirect blocks here: 689 * internal_dump_inode can only dump them from the main 690 * on-disk inode, not from the journaled copy of the 691 * inode. */ 692 693 fprintf (out_file, " Blocks: "); 694 first = prev = start_extent = -1; 695 696 for (i=0; i<EXT2_NDIR_BLOCKS; i++) { 697 this = inode->i_block[i]; 698 if (start_extent >= 0 && this == prev+1) { 699 prev = this; 700 continue; 701 } else { 702 show_extent(out_file, start_extent, i, first); 703 start_extent = i; 704 first = prev = this; 705 } 706 } 707 show_extent(out_file, start_extent, i, first); 708 show_indirect(out_file, "IND", inode->i_block[i++]); 709 show_indirect(out_file, "DIND", inode->i_block[i++]); 710 show_indirect(out_file, "TIND", inode->i_block[i++]); 711 712 fprintf(out_file, "\n"); 713 } 714 715 if (dump_contents) 716 do_hexdump(out_file, buf, blocksize); 717 718 } 719 720 static void do_hexdump (FILE *out_file, char *buf, int blocksize) 721 { 722 int i,j; 723 int *intp; 724 char *charp; 725 unsigned char c; 726 727 intp = (int *) buf; 728 charp = (char *) buf; 729 730 for (i=0; i<blocksize; i+=16) { 731 fprintf(out_file, " %04x: ", i); 732 for (j=0; j<16; j+=4) 733 fprintf(out_file, "%08x ", *intp++); 734 for (j=0; j<16; j++) { 735 c = *charp++; 736 if (c < ' ' || c >= 127) 737 c = '.'; 738 fprintf(out_file, "%c", c); 739 } 740 fprintf(out_file, "\n"); 741 } 742 } 743 744