Whamcloud - gitweb
Add support for the half-MD4 HTREE hash.
[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                 if (debugfs_read_inode(journal_inum, &journal_inode, argv[0]))
207                         return;
208
209                 retval = ext2fs_file_open(current_fs, journal_inum,
210                                           0, &journal_file);
211                 if (retval) {
212                         com_err(argv[0], retval, "while opening ext2 file");
213                         return;
214                 }
215                 journal_source.where = JOURNAL_IS_INTERNAL;
216                 journal_source.file = journal_file;
217         } else if ((journal_fn =
218             ext2fs_find_block_device(current_fs->super->s_journal_dev))) {
219                 journal_fd = open(journal_fn, O_RDONLY, 0);
220                 if (journal_fd < 0) {
221                         com_err(argv[0], errno, "while opening %s for logdump",
222                                 journal_fn);
223                         free(journal_fn);
224                         return;
225                 }
226                 fprintf(out_file, "Using external journal found at %s\n",
227                         journal_fn);
228                 free(journal_fn);
229                 journal_source.where = JOURNAL_IS_EXTERNAL;
230                 journal_source.fd = journal_fd;
231         } else {
232                 com_err(argv[0], 0, "filesystem has no journal");
233                 return;
234         }
235
236         dump_journal(argv[0], out_file, &journal_source);
237
238         if (journal_source.where == JOURNAL_IS_INTERNAL)
239                 ext2fs_file_close(journal_file);
240         else
241                 close(journal_fd);
242
243         if (out_file != stdout)
244                 fclose(out_file);
245
246         return;
247 }
248
249
250 static int read_journal_block(const char *cmd, struct journal_source *source, 
251                               off_t offset, char *buf, int size,
252                               unsigned int *got)
253 {
254         int retval;
255         
256         if (source->where == JOURNAL_IS_EXTERNAL) {
257                 if (lseek(source->fd, offset, SEEK_SET) < 0) {
258                         retval = errno;
259                         com_err(cmd, retval, "while seeking in reading journal");
260                         return retval;
261                 }
262                 retval = read(source->fd, buf, size);
263                 if (retval >= 0) {
264                         *got = retval;
265                         retval = 0;
266                 } else
267                         retval = errno;
268         } else {
269                 retval = ext2fs_file_lseek(source->file, offset, 
270                                            EXT2_SEEK_SET, NULL);
271                 if (retval) {
272                         com_err(cmd, retval, "while seeking in reading journal");
273                         return retval;
274                 }
275                 
276                 retval = ext2fs_file_read(source->file, buf, size, got);
277         }
278         
279         if (retval)
280                 com_err(cmd, retval, "while while reading journal");
281         else if (*got != size) {
282                 com_err(cmd, 0, "short read (read %d, expected %d) while while reading journal", *got, size);
283                 retval = -1;
284         }
285         
286         return retval;
287 }
288
289 static const char *type_to_name(int btype)
290 {
291         switch (btype) {
292         case JFS_DESCRIPTOR_BLOCK:
293                 return "descriptor block";
294         case JFS_COMMIT_BLOCK:
295                 return "commit block";
296         case JFS_SUPERBLOCK_V1:
297                 return "V1 superblock";
298         case JFS_SUPERBLOCK_V2:
299                 return "V2 superblock";
300         case JFS_REVOKE_BLOCK:
301                 return "revoke table";
302         }
303         return "unrecognised type";
304 }
305
306
307 static void dump_journal(char *cmdname, FILE *out_file, 
308                          struct journal_source *source)
309 {
310         struct ext2_super_block *sb;
311         char                    jsb_buffer[1024];
312         char                    buf[8192];
313         journal_superblock_t    *jsb;
314         int                     blocksize = 1024;
315         unsigned int            got;
316         int                     retval;
317         __u32                   magic, sequence, blocktype;
318         journal_header_t        *header;
319         
320         tid_t                   transaction;
321         unsigned int            blocknr = 0;
322         
323         /* First, check to see if there's an ext2 superblock header */
324         retval = read_journal_block(cmdname, source, 0, 
325                                     buf, 2048, &got);
326         if (retval)
327                 return;
328
329         jsb = (journal_superblock_t *) buf;
330         sb = (struct ext2_super_block *) (buf+1024);
331 #ifdef ENABLE_SWAPFS
332         if (sb->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC)) 
333                 ext2fs_swap_super(sb);
334 #endif
335         
336         if ((be32_to_cpu(jsb->s_header.h_magic) != JFS_MAGIC_NUMBER) &&
337             (sb->s_magic == EXT2_SUPER_MAGIC) &&
338             (sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) {
339                 blocksize = EXT2_BLOCK_SIZE(sb);
340                 blocknr = (blocksize == 1024) ? 2 : 1;
341                 uuid_unparse(sb->s_uuid, jsb_buffer);
342                 fprintf(out_file, "Ext2 superblock header found.\n");
343                 if (dump_all) {
344                         fprintf(out_file, "\tuuid=%s\n", jsb_buffer);
345                         fprintf(out_file, "\tblocksize=%d\n", blocksize);
346                         fprintf(out_file, "\tjournal data size %ld\n",
347                                 (long) sb->s_blocks_count);
348                 }
349         }
350         
351         /* Next, read the journal superblock */
352
353         retval = read_journal_block(cmdname, source, blocknr*blocksize, 
354                                     jsb_buffer, 1024, &got);
355         if (retval)
356                 return;
357
358         jsb = (journal_superblock_t *) jsb_buffer;
359         if (be32_to_cpu(jsb->s_header.h_magic) != JFS_MAGIC_NUMBER) {
360                 fprintf(out_file,
361                         "Journal superblock magic number invalid!\n");
362                 return;
363         }
364         blocksize = be32_to_cpu(jsb->s_blocksize);
365         transaction = be32_to_cpu(jsb->s_sequence);
366         blocknr = be32_to_cpu(jsb->s_start);
367
368         fprintf(out_file, "Journal starts at block %u, transaction %u\n",
369                 blocknr, transaction);
370
371         if (!blocknr)
372                 /* Empty journal, nothing to do. */
373                 return;
374                 
375         while (1) {
376                 retval = read_journal_block(cmdname, source, 
377                                             blocknr*blocksize, buf,
378                                             blocksize, &got);
379                 if (retval || got != blocksize)
380                         return;
381         
382                 header = (journal_header_t *) buf;
383
384                 magic = be32_to_cpu(header->h_magic);
385                 sequence = be32_to_cpu(header->h_sequence);
386                 blocktype = be32_to_cpu(header->h_blocktype);
387                 
388                 if (magic != JFS_MAGIC_NUMBER) {
389                         fprintf (out_file, "No magic number at block %u: "
390                                  "end of journal.\n", blocknr);
391                         return;
392                 }
393                 
394                 if (sequence != transaction) {
395                         fprintf (out_file, "Found sequence %u (not %u) at "
396                                  "block %u: end of journal.\n", 
397                                  sequence, transaction, blocknr);
398                         return;
399                 }
400
401                 if (dump_descriptors) {
402                         fprintf (out_file, "Found expected sequence %u, "
403                                  "type %u (%s) at block %u\n",
404                                  sequence, blocktype, 
405                                  type_to_name(blocktype), blocknr);
406                 }
407                 
408                 switch (blocktype) {
409                 case JFS_DESCRIPTOR_BLOCK:
410                         dump_descriptor_block(out_file, source, buf, jsb, 
411                                               &blocknr, blocksize,
412                                               transaction);
413                         continue;
414
415                 case JFS_COMMIT_BLOCK:
416                         transaction++;
417                         blocknr++;
418                         WRAP(jsb, blocknr);
419                         continue;
420                         
421                 case JFS_REVOKE_BLOCK:
422                         dump_revoke_block(out_file, buf, jsb,
423                                           blocknr, blocksize, 
424                                           transaction);
425                         blocknr++;
426                         WRAP(jsb, blocknr);
427                         continue;
428
429                 default:
430                         fprintf (out_file, "Unexpected block type %u at "
431                                  "block %u.\n", blocktype, blocknr);
432                         return;
433                 }
434         }
435 }
436
437
438 static void dump_descriptor_block(FILE *out_file, 
439                                   struct journal_source *source, 
440                                   char *buf,
441                                   journal_superblock_t *jsb, 
442                                   unsigned int *blockp, int blocksize,
443                                   tid_t transaction)
444 {
445         int                     offset;
446         char                    *tagp;
447         journal_block_tag_t     *tag;
448         unsigned int            blocknr;
449         __u32                   tag_block;
450         __u32                   tag_flags;
451                 
452
453         offset = sizeof(journal_header_t);
454         blocknr = *blockp;
455
456         if (dump_all) 
457                 fprintf(out_file, "Dumping descriptor block, sequence %u, at "
458                         "block %u:\n", transaction, blocknr);
459         
460         ++blocknr;
461         WRAP(jsb, blocknr);
462         
463         do {
464                 /* Work out the location of the current tag, and skip to 
465                  * the next one... */
466                 tagp = &buf[offset];
467                 tag = (journal_block_tag_t *) tagp;
468                 offset += sizeof(journal_block_tag_t);
469
470                 /* ... and if we have gone too far, then we've reached the
471                    end of this block. */
472                 if (offset > blocksize)
473                         break;
474         
475                 tag_block = be32_to_cpu(tag->t_blocknr);
476                 tag_flags = be32_to_cpu(tag->t_flags);
477
478                 if (!(tag_flags & JFS_FLAG_SAME_UUID))
479                         offset += 16;
480
481                 dump_metadata_block(out_file, source, jsb, 
482                                     blocknr, tag_block, blocksize, 
483                                     transaction);
484
485                 ++blocknr;
486                 WRAP(jsb, blocknr);
487                 
488         } while (!(tag_flags & JFS_FLAG_LAST_TAG));
489         
490         *blockp = blocknr;
491 }
492
493
494 static void dump_revoke_block(FILE *out_file, char *buf,
495                                   journal_superblock_t *jsb, 
496                                   unsigned int blocknr, int blocksize,
497                                   tid_t transaction)
498 {
499         int                     offset, max;
500         journal_revoke_header_t *header;
501         unsigned int            *entry, rblock;
502         
503         if (dump_all) 
504                 fprintf(out_file, "Dumping revoke block, sequence %u, at "
505                         "block %u:\n", transaction, blocknr);
506         
507         header = (journal_revoke_header_t *) buf;
508         offset = sizeof(journal_revoke_header_t);
509         max = be32_to_cpu(header->r_count);
510
511         while (offset < max) {
512                 entry = (unsigned int *) (buf + offset);
513                 rblock = be32_to_cpu(*entry);
514                 if (dump_all || rblock == block_to_dump) {
515                         fprintf(out_file, "  Revoke FS block %u", rblock);
516                         if (dump_all)
517                                 fprintf(out_file, "\n");
518                         else
519                                 fprintf(out_file," at block %u, sequence %u\n",
520                                         blocknr, transaction);
521                 }
522                 offset += 4;
523         }
524 }
525
526
527 static void show_extent(FILE *out_file, int start_extent, int end_extent,
528                         __u32 first_block)
529 {
530         if (start_extent >= 0 && first_block != 0)
531                 fprintf(out_file, "(%d+%u): %u ", 
532                         start_extent, end_extent-start_extent, first_block);
533 }
534
535 static void show_indirect(FILE *out_file, const char *name, __u32 where)
536 {
537         if (where)
538                 fprintf(out_file, "(%s): %u ", name, where);
539 }
540
541
542 static void dump_metadata_block(FILE *out_file, struct journal_source *source,
543                                 journal_superblock_t *jsb,
544                                 unsigned int log_blocknr, 
545                                 unsigned int fs_blocknr, 
546                                 int blocksize,
547                                 tid_t transaction)
548 {
549         unsigned int    got;
550         int             retval;
551         char            buf[8192];
552         
553         if (!(dump_all
554               || (fs_blocknr == block_to_dump)
555               || (fs_blocknr == inode_block_to_dump)
556               || (fs_blocknr == bitmap_to_dump)))
557                 return;
558         
559         fprintf(out_file, "  FS block %u logged at ", fs_blocknr);
560         if (!dump_all) 
561                 fprintf(out_file, "sequence %u, ", transaction);
562         fprintf(out_file, "journal block %u\n", log_blocknr);
563         
564         /* There are two major special cases to parse:
565          * 
566          * If this block is a block
567          * bitmap block, we need to give it special treatment so that we
568          * can log any allocates and deallocates which affect the
569          * block_to_dump query block. 
570          * 
571          * If the block is an inode block for the inode being searched
572          * for, then we need to dump the contents of that inode
573          * structure symbolically.  
574          */
575         
576         if (!(dump_contents && dump_all)
577             && fs_blocknr != block_to_dump
578             && fs_blocknr != bitmap_to_dump 
579             && fs_blocknr != inode_block_to_dump)
580                 return;
581         
582         retval = read_journal_block("logdump", source, 
583                                     blocksize * log_blocknr,
584                                     buf, blocksize, &got);
585         if (retval)
586                 return;
587         
588         if (fs_blocknr == bitmap_to_dump) {
589                 struct ext2_super_block *super;
590                 int offset;
591                 
592                 super = current_fs->super;
593                 offset = ((fs_blocknr - super->s_first_data_block) %
594                           super->s_blocks_per_group);
595         
596                 fprintf(out_file, "    (block bitmap for block %u: "
597                         "block is %s)\n", 
598                         block_to_dump,
599                         ext2fs_test_bit(offset, buf) ? "SET" : "CLEAR");
600         }
601         
602         if (fs_blocknr == inode_block_to_dump) {
603                 struct ext2_inode *inode;
604                 int first, prev, this, start_extent, i;
605                 
606                 fprintf(out_file, "    (inode block for inode %u):\n",
607                         inode_to_dump);
608                 
609                 inode = (struct ext2_inode *) (buf + inode_offset_to_dump);
610                 internal_dump_inode(out_file, "    ", inode_to_dump, inode, 0);
611                 
612                 /* Dump out the direct/indirect blocks here:
613                  * internal_dump_inode can only dump them from the main
614                  * on-disk inode, not from the journaled copy of the
615                  * inode. */
616                 
617                 fprintf (out_file, "    Blocks:  ");
618                 first = prev = start_extent = -1;
619
620                 for (i=0; i<EXT2_NDIR_BLOCKS; i++) {
621                         this = inode->i_block[i];
622                         if (start_extent >= 0  && this == prev+1) {
623                                 prev = this;
624                                 continue;
625                         } else {
626                                 show_extent(out_file, start_extent, i, first);
627                                 start_extent = i;
628                                 first = prev = this;
629                         }
630                 }
631                 show_extent(out_file, start_extent, i, first);
632                 show_indirect(out_file, "IND", inode->i_block[i++]);
633                 show_indirect(out_file, "DIND", inode->i_block[i++]);
634                 show_indirect(out_file, "TIND", inode->i_block[i++]);
635                 
636                 fprintf(out_file, "\n");
637         }
638
639         if (dump_contents)
640                 do_hexdump(out_file, buf, blocksize);
641         
642 }
643
644 static void do_hexdump (FILE *out_file, char *buf, int blocksize)
645 {
646         int i,j;
647         int *intp;
648         char *charp;
649         unsigned char c;
650         
651         intp = (int *) buf;
652         charp = (char *) buf;
653         
654         for (i=0; i<blocksize; i+=16) {
655                 fprintf(out_file, "    %04x:  ", i);
656                 for (j=0; j<16; j+=4)
657                         fprintf(out_file, "%08x ", *intp++);
658                 for (j=0; j<16; j++) {
659                         c = *charp++;
660                         if (c < ' ' || c >= 127)
661                                 c = '.';
662                         fprintf(out_file, "%c", c);
663                 }
664                 fprintf(out_file, "\n");
665         }
666 }
667