]>
Commit | Line | Data |
---|---|---|
da81e3fc TT |
1 | /* |
2 | * logdump.c --- dump the contents of the journal out to a file | |
efc6f628 | 3 | * |
055866d8 | 4 | * Author: Stephen C. Tweedie, 2001 <sct@redhat.com> |
da81e3fc | 5 | * Copyright (C) 2001 Red Hat, Inc. |
efc6f628 | 6 | * Based on portions Copyright (C) 1994 Theodore Ts'o. |
da81e3fc | 7 | * |
efc6f628 | 8 | * This file may be redistributed under the terms of the GNU Public |
da81e3fc TT |
9 | * License. |
10 | */ | |
11 | ||
d1154eb4 | 12 | #include "config.h" |
da81e3fc TT |
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> | |
efc6f628 | 28 | #else |
da81e3fc TT |
29 | extern int optind; |
30 | extern char *optarg; | |
31 | #endif | |
da81e3fc TT |
32 | |
33 | #include "debugfs.h" | |
f364093b | 34 | #include "blkid/blkid.h" |
da81e3fc | 35 | #include "jfs_user.h" |
4ea7bd04 | 36 | #include <uuid/uuid.h> |
da81e3fc TT |
37 | |
38 | enum journal_location {JOURNAL_IS_INTERNAL, JOURNAL_IS_EXTERNAL}; | |
39 | ||
4dbfd79d | 40 | #define ANY_BLOCK ((blk64_t) -1) |
54434927 | 41 | |
03a179a1 | 42 | static int dump_all, dump_super, dump_old, dump_contents, dump_descriptors; |
f404167d TT |
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; | |
da81e3fc | 46 | |
efc6f628 | 47 | struct journal_source |
da81e3fc TT |
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 *, | |
efc6f628 | 64 | journal_superblock_t*, |
98446d73 TT |
65 | unsigned int, unsigned int, unsigned int, |
66 | int, tid_t); | |
da81e3fc TT |
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 | ||
2fcbcb1b TT |
75 | void do_logdump(int argc, char **argv, int sci_idx EXT2FS_ATTR((unused)), |
76 | void *infop EXT2FS_ATTR((unused))) | |
da81e3fc | 77 | { |
da81e3fc | 78 | int c; |
da81e3fc TT |
79 | int retval; |
80 | char *out_fn; | |
81 | FILE *out_file; | |
efc6f628 | 82 | |
da81e3fc TT |
83 | char *inode_spec = NULL; |
84 | char *journal_fn = NULL; | |
85 | int journal_fd = 0; | |
a435ec34 | 86 | int use_sb = 0; |
da81e3fc TT |
87 | ext2_ino_t journal_inum; |
88 | struct ext2_inode journal_inode; | |
89 | ext2_file_t journal_file; | |
da81e3fc | 90 | char *tmp; |
5e4f0709 | 91 | struct journal_source journal_source; |
f364093b | 92 | struct ext2_super_block *es = NULL; |
efc6f628 | 93 | |
c5977630 | 94 | journal_source.where = JOURNAL_IS_INTERNAL; |
5e4f0709 TT |
95 | journal_source.fd = 0; |
96 | journal_source.file = 0; | |
da81e3fc | 97 | dump_all = 0; |
46272d5a | 98 | dump_old = 0; |
da81e3fc | 99 | dump_contents = 0; |
03a179a1 | 100 | dump_super = 0; |
da81e3fc | 101 | dump_descriptors = 1; |
54434927 | 102 | block_to_dump = ANY_BLOCK; |
da81e3fc | 103 | bitmap_to_dump = -1; |
54434927 | 104 | inode_block_to_dump = ANY_BLOCK; |
da81e3fc | 105 | inode_to_dump = -1; |
efc6f628 | 106 | |
88494bb6 | 107 | reset_getopt(); |
03a179a1 | 108 | while ((c = getopt (argc, argv, "ab:ci:f:OsS")) != EOF) { |
da81e3fc TT |
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; | |
46272d5a DW |
132 | case 'O': |
133 | dump_old++; | |
134 | break; | |
a435ec34 TT |
135 | case 's': |
136 | use_sb++; | |
137 | break; | |
03a179a1 TT |
138 | case 'S': |
139 | dump_super++; | |
140 | break; | |
da81e3fc | 141 | default: |
9b9a780f | 142 | goto print_usage; |
da81e3fc TT |
143 | } |
144 | } | |
145 | if (optind != argc && optind != argc-1) { | |
9b9a780f | 146 | goto print_usage; |
da81e3fc TT |
147 | } |
148 | ||
f364093b TT |
149 | if (current_fs) |
150 | es = current_fs->super; | |
151 | ||
da81e3fc TT |
152 | if (inode_spec) { |
153 | int inode_group, group_offset, inodes_per_block; | |
f364093b | 154 | |
da81e3fc TT |
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) | |
f364093b | 163 | / es->s_inodes_per_group); |
da81e3fc | 164 | group_offset = ((inode_to_dump - 1) |
f364093b | 165 | % es->s_inodes_per_group); |
efc6f628 | 166 | inodes_per_block = (current_fs->blocksize |
da81e3fc | 167 | / sizeof(struct ext2_inode)); |
efc6f628 TT |
168 | |
169 | inode_block_to_dump = | |
048786d7 | 170 | ext2fs_inode_table_loc(current_fs, inode_group) + |
da81e3fc TT |
171 | (group_offset / inodes_per_block); |
172 | inode_offset_to_dump = ((group_offset % inodes_per_block) | |
173 | * sizeof(struct ext2_inode)); | |
4dbfd79d | 174 | printf("Inode %u is at group %u, block %llu, offset %u\n", |
da81e3fc TT |
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"); | |
0bed54fd | 184 | if (!out_file) { |
da81e3fc TT |
185 | com_err(argv[0], errno, "while opening %s for logdump", |
186 | out_fn); | |
0bed54fd | 187 | goto errout; |
da81e3fc TT |
188 | } |
189 | } | |
190 | ||
54434927 | 191 | if (block_to_dump != ANY_BLOCK && current_fs != NULL) { |
efc6f628 | 192 | group_to_dump = ((block_to_dump - |
f364093b TT |
193 | es->s_first_data_block) |
194 | / es->s_blocks_per_group); | |
048786d7 | 195 | bitmap_to_dump = ext2fs_block_bitmap_loc(current_fs, group_to_dump); |
da81e3fc | 196 | } |
da81e3fc | 197 | |
5faba3ac | 198 | if (!journal_fn && check_fs_open(argv[0])) |
0bed54fd | 199 | goto errout; |
5faba3ac TT |
200 | |
201 | if (journal_fn) { | |
da81e3fc TT |
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); | |
0bed54fd | 207 | goto errout; |
da81e3fc | 208 | } |
efc6f628 | 209 | |
da81e3fc TT |
210 | journal_source.where = JOURNAL_IS_EXTERNAL; |
211 | journal_source.fd = journal_fd; | |
f364093b | 212 | } else if ((journal_inum = es->s_journal_inum)) { |
a435ec34 TT |
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"); | |
0bed54fd | 217 | goto errout; |
a435ec34 TT |
218 | } |
219 | memset(&journal_inode, 0, sizeof(struct ext2_inode)); | |
efc6f628 | 220 | memcpy(&journal_inode.i_block[0], es->s_jnl_blocks, |
a435ec34 | 221 | EXT2_N_BLOCKS*4); |
931b58e1 | 222 | journal_inode.i_size_high = es->s_jnl_blocks[15]; |
a435ec34 TT |
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 { | |
efc6f628 | 227 | if (debugfs_read_inode(journal_inum, &journal_inode, |
a435ec34 | 228 | argv[0])) |
0bed54fd | 229 | goto errout; |
a435ec34 | 230 | } |
efc6f628 | 231 | |
a435ec34 TT |
232 | retval = ext2fs_file_open2(current_fs, journal_inum, |
233 | &journal_inode, 0, &journal_file); | |
da81e3fc TT |
234 | if (retval) { |
235 | com_err(argv[0], retval, "while opening ext2 file"); | |
0bed54fd | 236 | goto errout; |
da81e3fc | 237 | } |
da81e3fc TT |
238 | journal_source.where = JOURNAL_IS_INTERNAL; |
239 | journal_source.file = journal_file; | |
f364093b TT |
240 | } else { |
241 | char uuid[37]; | |
efc6f628 | 242 | |
f364093b TT |
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"); | |
0bed54fd | 249 | goto errout; |
f364093b | 250 | } |
5faba3ac TT |
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); | |
0bed54fd | 256 | goto errout; |
5faba3ac TT |
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; | |
da81e3fc TT |
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 | ||
0bed54fd | 272 | errout: |
dad0bab2 | 273 | if (out_file && (out_file != stdout)) |
da81e3fc TT |
274 | fclose(out_file); |
275 | ||
276 | return; | |
9b9a780f TT |
277 | |
278 | print_usage: | |
7600aa0f | 279 | fprintf(stderr, "%s: Usage: logdump [-acsOS] [-b<block>] [-i<filespec>]\n\t" |
9b9a780f | 280 | "[-f<journal_file>] [output_file]\n", argv[0]); |
da81e3fc TT |
281 | } |
282 | ||
283 | ||
efc6f628 | 284 | static int read_journal_block(const char *cmd, struct journal_source *source, |
5eca88c1 | 285 | ext2_loff_t offset, char *buf, unsigned int size) |
da81e3fc TT |
286 | { |
287 | int retval; | |
b34b94d5 | 288 | unsigned int got; |
efc6f628 | 289 | |
da81e3fc | 290 | if (source->where == JOURNAL_IS_EXTERNAL) { |
4bb0c043 TT |
291 | if (lseek(source->fd, offset, SEEK_SET) < 0) { |
292 | retval = errno; | |
b34b94d5 | 293 | goto seek_err; |
4bb0c043 TT |
294 | } |
295 | retval = read(source->fd, buf, size); | |
b34b94d5 | 296 | if (retval < 0) { |
5faba3ac | 297 | retval = errno; |
b34b94d5 TT |
298 | goto read_err; |
299 | } | |
300 | got = retval; | |
301 | retval = 0; | |
da81e3fc | 302 | } else { |
5eca88c1 TT |
303 | retval = ext2fs_file_llseek(source->file, offset, |
304 | EXT2_SEEK_SET, NULL); | |
da81e3fc | 305 | if (retval) { |
b34b94d5 | 306 | seek_err: |
da81e3fc TT |
307 | com_err(cmd, retval, "while seeking in reading journal"); |
308 | return retval; | |
309 | } | |
b34b94d5 TT |
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 | } | |
da81e3fc | 316 | } |
b34b94d5 TT |
317 | if (got != size) { |
318 | com_err(cmd, 0, "short read (read %u, expected %u) " | |
319 | "while reading journal", got, size); | |
da81e3fc TT |
320 | retval = -1; |
321 | } | |
da81e3fc TT |
322 | return retval; |
323 | } | |
324 | ||
5e4f0709 | 325 | static const char *type_to_name(int btype) |
da81e3fc TT |
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"; | |
da81e3fc TT |
338 | } |
339 | return "unrecognised type"; | |
340 | } | |
341 | ||
342 | ||
efc6f628 | 343 | static void dump_journal(char *cmdname, FILE *out_file, |
da81e3fc TT |
344 | struct journal_source *source) |
345 | { | |
5faba3ac | 346 | struct ext2_super_block *sb; |
da81e3fc TT |
347 | char jsb_buffer[1024]; |
348 | char buf[8192]; | |
349 | journal_superblock_t *jsb; | |
54434927 | 350 | unsigned int blocksize = 1024; |
da81e3fc TT |
351 | int retval; |
352 | __u32 magic, sequence, blocktype; | |
353 | journal_header_t *header; | |
da81e3fc | 354 | tid_t transaction; |
5faba3ac | 355 | unsigned int blocknr = 0; |
efc6f628 | 356 | |
5faba3ac | 357 | /* First, check to see if there's an ext2 superblock header */ |
b34b94d5 | 358 | retval = read_journal_block(cmdname, source, 0, buf, 2048); |
da81e3fc TT |
359 | if (retval) |
360 | return; | |
5faba3ac TT |
361 | |
362 | jsb = (journal_superblock_t *) buf; | |
363 | sb = (struct ext2_super_block *) (buf+1024); | |
2eae0930 | 364 | #ifdef WORDS_BIGENDIAN |
efc6f628 | 365 | if (sb->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC)) |
5faba3ac | 366 | ext2fs_swap_super(sb); |
ff63f268 | 367 | #endif |
efc6f628 | 368 | |
5faba3ac TT |
369 | if ((be32_to_cpu(jsb->s_header.h_magic) != JFS_MAGIC_NUMBER) && |
370 | (sb->s_magic == EXT2_SUPER_MAGIC) && | |
4ee26699 | 371 | ext2fs_has_feature_journal_dev(sb)) { |
5faba3ac TT |
372 | blocksize = EXT2_BLOCK_SIZE(sb); |
373 | blocknr = (blocksize == 1024) ? 2 : 1; | |
4ea7bd04 | 374 | uuid_unparse(sb->s_uuid, jsb_buffer); |
5faba3ac TT |
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); | |
5d38ef1d | 379 | fprintf(out_file, "\tjournal data size %lu\n", |
4efbac6f | 380 | (unsigned long) ext2fs_blocks_count(sb)); |
5faba3ac TT |
381 | } |
382 | } | |
efc6f628 | 383 | |
5faba3ac | 384 | /* Next, read the journal superblock */ |
5eca88c1 TT |
385 | retval = read_journal_block(cmdname, source, |
386 | ((ext2_loff_t) blocknr) * blocksize, | |
b34b94d5 | 387 | jsb_buffer, 1024); |
5faba3ac TT |
388 | if (retval) |
389 | return; | |
390 | ||
03a179a1 TT |
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 | ||
da81e3fc | 397 | jsb = (journal_superblock_t *) jsb_buffer; |
5faba3ac TT |
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 | } | |
da81e3fc TT |
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 | ||
46272d5a | 410 | if (!blocknr) { |
da81e3fc | 411 | /* Empty journal, nothing to do. */ |
46272d5a DW |
412 | if (!dump_old) |
413 | return; | |
414 | else | |
415 | blocknr = 1; | |
416 | } | |
efc6f628 | 417 | |
da81e3fc | 418 | while (1) { |
efc6f628 | 419 | retval = read_journal_block(cmdname, source, |
5eca88c1 TT |
420 | ((ext2_loff_t) blocknr) * blocksize, |
421 | buf, blocksize); | |
b34b94d5 | 422 | if (retval) |
da81e3fc | 423 | return; |
efc6f628 | 424 | |
da81e3fc TT |
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); | |
efc6f628 | 430 | |
da81e3fc TT |
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 | } | |
efc6f628 | 436 | |
da81e3fc TT |
437 | if (sequence != transaction) { |
438 | fprintf (out_file, "Found sequence %u (not %u) at " | |
efc6f628 | 439 | "block %u: end of journal.\n", |
da81e3fc | 440 | sequence, transaction, blocknr); |
46272d5a DW |
441 | if (!dump_old) |
442 | return; | |
da81e3fc TT |
443 | } |
444 | ||
445 | if (dump_descriptors) { | |
446 | fprintf (out_file, "Found expected sequence %u, " | |
447 | "type %u (%s) at block %u\n", | |
efc6f628 | 448 | sequence, blocktype, |
da81e3fc TT |
449 | type_to_name(blocktype), blocknr); |
450 | } | |
efc6f628 | 451 | |
da81e3fc TT |
452 | switch (blocktype) { |
453 | case JFS_DESCRIPTOR_BLOCK: | |
efc6f628 | 454 | dump_descriptor_block(out_file, source, buf, jsb, |
da81e3fc TT |
455 | &blocknr, blocksize, |
456 | transaction); | |
457 | continue; | |
458 | ||
459 | case JFS_COMMIT_BLOCK: | |
460 | transaction++; | |
461 | blocknr++; | |
462 | WRAP(jsb, blocknr); | |
463 | continue; | |
efc6f628 | 464 | |
da81e3fc TT |
465 | case JFS_REVOKE_BLOCK: |
466 | dump_revoke_block(out_file, buf, jsb, | |
efc6f628 | 467 | blocknr, blocksize, |
da81e3fc TT |
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 | ||
df0b907e TT |
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 | } | |
da81e3fc | 498 | |
efc6f628 TT |
499 | static void dump_descriptor_block(FILE *out_file, |
500 | struct journal_source *source, | |
da81e3fc | 501 | char *buf, |
efc6f628 | 502 | journal_superblock_t *jsb, |
da81e3fc TT |
503 | unsigned int *blockp, int blocksize, |
504 | tid_t transaction) | |
505 | { | |
38d5adf3 | 506 | int offset, tag_size, csum_size = 0; |
da81e3fc TT |
507 | char *tagp; |
508 | journal_block_tag_t *tag; | |
509 | unsigned int blocknr; | |
510 | __u32 tag_block; | |
511 | __u32 tag_flags; | |
efc6f628 | 512 | |
38d5adf3 | 513 | tag_size = journal_super_tag_bytes(jsb); |
da81e3fc TT |
514 | offset = sizeof(journal_header_t); |
515 | blocknr = *blockp; | |
516 | ||
38d5adf3 DW |
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 | ||
efc6f628 | 521 | if (dump_all) |
da81e3fc TT |
522 | fprintf(out_file, "Dumping descriptor block, sequence %u, at " |
523 | "block %u:\n", transaction, blocknr); | |
efc6f628 | 524 | |
da81e3fc TT |
525 | ++blocknr; |
526 | WRAP(jsb, blocknr); | |
efc6f628 | 527 | |
da81e3fc | 528 | do { |
efc6f628 | 529 | /* Work out the location of the current tag, and skip to |
da81e3fc TT |
530 | * the next one... */ |
531 | tagp = &buf[offset]; | |
532 | tag = (journal_block_tag_t *) tagp; | |
98446d73 | 533 | offset += tag_size; |
da81e3fc TT |
534 | |
535 | /* ... and if we have gone too far, then we've reached the | |
536 | end of this block. */ | |
38d5adf3 | 537 | if (offset > blocksize - csum_size) |
da81e3fc | 538 | break; |
efc6f628 | 539 | |
da81e3fc | 540 | tag_block = be32_to_cpu(tag->t_blocknr); |
2556373a | 541 | tag_flags = be16_to_cpu(tag->t_flags); |
da81e3fc TT |
542 | |
543 | if (!(tag_flags & JFS_FLAG_SAME_UUID)) | |
544 | offset += 16; | |
545 | ||
efc6f628 | 546 | dump_metadata_block(out_file, source, jsb, |
98446d73 | 547 | blocknr, tag_block, tag_flags, blocksize, |
da81e3fc TT |
548 | transaction); |
549 | ||
550 | ++blocknr; | |
551 | WRAP(jsb, blocknr); | |
efc6f628 | 552 | |
da81e3fc | 553 | } while (!(tag_flags & JFS_FLAG_LAST_TAG)); |
efc6f628 | 554 | |
da81e3fc TT |
555 | *blockp = blocknr; |
556 | } | |
557 | ||
558 | ||
559 | static void dump_revoke_block(FILE *out_file, char *buf, | |
efc6f628 TT |
560 | journal_superblock_t *jsb EXT2FS_ATTR((unused)), |
561 | unsigned int blocknr, | |
54434927 TT |
562 | int blocksize EXT2FS_ATTR((unused)), |
563 | tid_t transaction) | |
da81e3fc TT |
564 | { |
565 | int offset, max; | |
566 | journal_revoke_header_t *header; | |
a1ff15f8 DW |
567 | unsigned long long rblock; |
568 | int tag_size = sizeof(__u32); | |
efc6f628 TT |
569 | |
570 | if (dump_all) | |
da81e3fc TT |
571 | fprintf(out_file, "Dumping revoke block, sequence %u, at " |
572 | "block %u:\n", transaction, blocknr); | |
efc6f628 | 573 | |
a1ff15f8 DW |
574 | if (be32_to_cpu(jsb->s_feature_incompat) & JFS_FEATURE_INCOMPAT_64BIT) |
575 | tag_size = sizeof(__u64); | |
576 | ||
da81e3fc TT |
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) { | |
a1ff15f8 DW |
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 | } | |
da81e3fc | 589 | if (dump_all || rblock == block_to_dump) { |
a1ff15f8 | 590 | fprintf(out_file, " Revoke FS block %llu", rblock); |
da81e3fc TT |
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 | } | |
a1ff15f8 | 597 | offset += tag_size; |
da81e3fc TT |
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) | |
efc6f628 | 606 | fprintf(out_file, "(%d+%u): %u ", |
da81e3fc TT |
607 | start_extent, end_extent-start_extent, first_block); |
608 | } | |
609 | ||
5e4f0709 | 610 | static void show_indirect(FILE *out_file, const char *name, __u32 where) |
da81e3fc TT |
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, | |
54434927 | 618 | journal_superblock_t *jsb EXT2FS_ATTR((unused)), |
efc6f628 TT |
619 | unsigned int log_blocknr, |
620 | unsigned int fs_blocknr, | |
98446d73 | 621 | unsigned int log_tag_flags, |
da81e3fc TT |
622 | int blocksize, |
623 | tid_t transaction) | |
624 | { | |
5e4f0709 TT |
625 | int retval; |
626 | char buf[8192]; | |
efc6f628 | 627 | |
da81e3fc TT |
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; | |
efc6f628 | 633 | |
da81e3fc | 634 | fprintf(out_file, " FS block %u logged at ", fs_blocknr); |
efc6f628 | 635 | if (!dump_all) |
da81e3fc | 636 | fprintf(out_file, "sequence %u, ", transaction); |
98446d73 TT |
637 | fprintf(out_file, "journal block %u (flags 0x%x)\n", log_blocknr, |
638 | log_tag_flags); | |
efc6f628 | 639 | |
da81e3fc | 640 | /* There are two major special cases to parse: |
efc6f628 | 641 | * |
da81e3fc TT |
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 | |
efc6f628 TT |
645 | * block_to_dump query block. |
646 | * | |
da81e3fc TT |
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 | |
efc6f628 | 649 | * structure symbolically. |
da81e3fc | 650 | */ |
efc6f628 | 651 | |
da81e3fc TT |
652 | if (!(dump_contents && dump_all) |
653 | && fs_blocknr != block_to_dump | |
efc6f628 | 654 | && fs_blocknr != bitmap_to_dump |
da81e3fc TT |
655 | && fs_blocknr != inode_block_to_dump) |
656 | return; | |
efc6f628 TT |
657 | |
658 | retval = read_journal_block("logdump", source, | |
5eca88c1 | 659 | ((ext2_loff_t) log_blocknr) * blocksize, |
b34b94d5 | 660 | buf, blocksize); |
da81e3fc TT |
661 | if (retval) |
662 | return; | |
efc6f628 | 663 | |
da81e3fc TT |
664 | if (fs_blocknr == bitmap_to_dump) { |
665 | struct ext2_super_block *super; | |
666 | int offset; | |
efc6f628 | 667 | |
da81e3fc | 668 | super = current_fs->super; |
de2c4778 | 669 | offset = ((block_to_dump - super->s_first_data_block) % |
da81e3fc | 670 | super->s_blocks_per_group); |
efc6f628 | 671 | |
4dbfd79d | 672 | fprintf(out_file, " (block bitmap for block %llu: " |
efc6f628 | 673 | "block is %s)\n", |
da81e3fc TT |
674 | block_to_dump, |
675 | ext2fs_test_bit(offset, buf) ? "SET" : "CLEAR"); | |
676 | } | |
efc6f628 | 677 | |
da81e3fc TT |
678 | if (fs_blocknr == inode_block_to_dump) { |
679 | struct ext2_inode *inode; | |
680 | int first, prev, this, start_extent, i; | |
efc6f628 | 681 | |
da81e3fc TT |
682 | fprintf(out_file, " (inode block for inode %u):\n", |
683 | inode_to_dump); | |
efc6f628 | 684 | |
da81e3fc TT |
685 | inode = (struct ext2_inode *) (buf + inode_offset_to_dump); |
686 | internal_dump_inode(out_file, " ", inode_to_dump, inode, 0); | |
efc6f628 | 687 | |
da81e3fc TT |
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. */ | |
efc6f628 | 692 | |
da81e3fc | 693 | fprintf (out_file, " Blocks: "); |
5e4f0709 | 694 | first = prev = start_extent = -1; |
da81e3fc TT |
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++]); | |
efc6f628 | 711 | |
da81e3fc TT |
712 | fprintf(out_file, "\n"); |
713 | } | |
714 | ||
715 | if (dump_contents) | |
716 | do_hexdump(out_file, buf, blocksize); | |
efc6f628 | 717 | |
da81e3fc TT |
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; | |
efc6f628 | 726 | |
da81e3fc TT |
727 | intp = (int *) buf; |
728 | charp = (char *) buf; | |
efc6f628 | 729 | |
da81e3fc TT |
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 |