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