]>
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" |
8968289b | 36 | #include "ext2fs/fast_commit.h" |
4ea7bd04 | 37 | #include <uuid/uuid.h> |
da81e3fc TT |
38 | |
39 | enum journal_location {JOURNAL_IS_INTERNAL, JOURNAL_IS_EXTERNAL}; | |
40 | ||
4dbfd79d | 41 | #define ANY_BLOCK ((blk64_t) -1) |
54434927 | 42 | |
03a179a1 | 43 | static int dump_all, dump_super, dump_old, dump_contents, dump_descriptors; |
f404167d TT |
44 | static blk64_t block_to_dump, bitmap_to_dump, inode_block_to_dump; |
45 | static unsigned int group_to_dump, inode_offset_to_dump; | |
46 | static ext2_ino_t inode_to_dump; | |
da81e3fc | 47 | |
efc6f628 | 48 | struct journal_source |
da81e3fc TT |
49 | { |
50 | enum journal_location where; | |
51 | int fd; | |
52 | ext2_file_t file; | |
53 | }; | |
54 | ||
55 | static void dump_journal(char *, FILE *, struct journal_source *); | |
56 | ||
57 | static void dump_descriptor_block(FILE *, struct journal_source *, | |
58 | char *, journal_superblock_t *, | |
59 | unsigned int *, int, tid_t); | |
60 | ||
61 | static void dump_revoke_block(FILE *, char *, journal_superblock_t *, | |
62 | unsigned int, int, tid_t); | |
63 | ||
64 | static void dump_metadata_block(FILE *, struct journal_source *, | |
efc6f628 | 65 | journal_superblock_t*, |
98446d73 TT |
66 | unsigned int, unsigned int, unsigned int, |
67 | int, tid_t); | |
da81e3fc | 68 | |
8968289b HS |
69 | static void dump_fc_block(FILE *out_file, char *buf, int blocksize, |
70 | int transaction, int *fc_done, int dump_old); | |
71 | ||
da81e3fc TT |
72 | static void do_hexdump (FILE *, char *, int); |
73 | ||
74 | #define WRAP(jsb, blocknr) \ | |
75 | if (blocknr >= be32_to_cpu((jsb)->s_maxlen)) \ | |
76 | blocknr -= (be32_to_cpu((jsb)->s_maxlen) - \ | |
77 | be32_to_cpu((jsb)->s_first)); | |
78 | ||
2fcbcb1b TT |
79 | void do_logdump(int argc, char **argv, int sci_idx EXT2FS_ATTR((unused)), |
80 | void *infop EXT2FS_ATTR((unused))) | |
da81e3fc | 81 | { |
da81e3fc | 82 | int c; |
da81e3fc TT |
83 | int retval; |
84 | char *out_fn; | |
85 | FILE *out_file; | |
efc6f628 | 86 | |
da81e3fc TT |
87 | char *inode_spec = NULL; |
88 | char *journal_fn = NULL; | |
89 | int journal_fd = 0; | |
a435ec34 | 90 | int use_sb = 0; |
da81e3fc TT |
91 | ext2_ino_t journal_inum; |
92 | struct ext2_inode journal_inode; | |
93 | ext2_file_t journal_file; | |
da81e3fc | 94 | char *tmp; |
5e4f0709 | 95 | struct journal_source journal_source; |
f364093b | 96 | struct ext2_super_block *es = NULL; |
efc6f628 | 97 | |
c5977630 | 98 | journal_source.where = JOURNAL_IS_INTERNAL; |
5e4f0709 TT |
99 | journal_source.fd = 0; |
100 | journal_source.file = 0; | |
da81e3fc | 101 | dump_all = 0; |
46272d5a | 102 | dump_old = 0; |
da81e3fc | 103 | dump_contents = 0; |
03a179a1 | 104 | dump_super = 0; |
da81e3fc | 105 | dump_descriptors = 1; |
54434927 | 106 | block_to_dump = ANY_BLOCK; |
da81e3fc | 107 | bitmap_to_dump = -1; |
54434927 | 108 | inode_block_to_dump = ANY_BLOCK; |
da81e3fc | 109 | inode_to_dump = -1; |
efc6f628 | 110 | |
88494bb6 | 111 | reset_getopt(); |
03a179a1 | 112 | while ((c = getopt (argc, argv, "ab:ci:f:OsS")) != EOF) { |
da81e3fc TT |
113 | switch (c) { |
114 | case 'a': | |
115 | dump_all++; | |
116 | break; | |
117 | case 'b': | |
118 | block_to_dump = strtoul(optarg, &tmp, 0); | |
119 | if (*tmp) { | |
120 | com_err(argv[0], 0, | |
121 | "Bad block number - %s", optarg); | |
122 | return; | |
123 | } | |
124 | dump_descriptors = 0; | |
125 | break; | |
126 | case 'c': | |
127 | dump_contents++; | |
128 | break; | |
129 | case 'f': | |
130 | journal_fn = optarg; | |
131 | break; | |
132 | case 'i': | |
133 | inode_spec = optarg; | |
134 | dump_descriptors = 0; | |
135 | break; | |
46272d5a DW |
136 | case 'O': |
137 | dump_old++; | |
138 | break; | |
a435ec34 TT |
139 | case 's': |
140 | use_sb++; | |
141 | break; | |
03a179a1 TT |
142 | case 'S': |
143 | dump_super++; | |
144 | break; | |
da81e3fc | 145 | default: |
9b9a780f | 146 | goto print_usage; |
da81e3fc TT |
147 | } |
148 | } | |
149 | if (optind != argc && optind != argc-1) { | |
9b9a780f | 150 | goto print_usage; |
da81e3fc TT |
151 | } |
152 | ||
f364093b TT |
153 | if (current_fs) |
154 | es = current_fs->super; | |
155 | ||
da81e3fc TT |
156 | if (inode_spec) { |
157 | int inode_group, group_offset, inodes_per_block; | |
f364093b | 158 | |
da81e3fc TT |
159 | if (check_fs_open(argv[0])) |
160 | return; | |
161 | ||
162 | inode_to_dump = string_to_inode(inode_spec); | |
163 | if (!inode_to_dump) | |
164 | return; | |
165 | ||
166 | inode_group = ((inode_to_dump - 1) | |
f364093b | 167 | / es->s_inodes_per_group); |
da81e3fc | 168 | group_offset = ((inode_to_dump - 1) |
f364093b | 169 | % es->s_inodes_per_group); |
efc6f628 | 170 | inodes_per_block = (current_fs->blocksize |
da81e3fc | 171 | / sizeof(struct ext2_inode)); |
efc6f628 TT |
172 | |
173 | inode_block_to_dump = | |
048786d7 | 174 | ext2fs_inode_table_loc(current_fs, inode_group) + |
da81e3fc TT |
175 | (group_offset / inodes_per_block); |
176 | inode_offset_to_dump = ((group_offset % inodes_per_block) | |
177 | * sizeof(struct ext2_inode)); | |
4dbfd79d | 178 | printf("Inode %u is at group %u, block %llu, offset %u\n", |
da81e3fc | 179 | inode_to_dump, inode_group, |
33b9a60c TT |
180 | (unsigned long long) inode_block_to_dump, |
181 | inode_offset_to_dump); | |
da81e3fc TT |
182 | } |
183 | ||
184 | if (optind == argc) { | |
185 | out_file = stdout; | |
186 | } else { | |
187 | out_fn = argv[optind]; | |
188 | out_file = fopen(out_fn, "w"); | |
0bed54fd | 189 | if (!out_file) { |
da81e3fc TT |
190 | com_err(argv[0], errno, "while opening %s for logdump", |
191 | out_fn); | |
0bed54fd | 192 | goto errout; |
da81e3fc TT |
193 | } |
194 | } | |
195 | ||
54434927 | 196 | if (block_to_dump != ANY_BLOCK && current_fs != NULL) { |
efc6f628 | 197 | group_to_dump = ((block_to_dump - |
f364093b TT |
198 | es->s_first_data_block) |
199 | / es->s_blocks_per_group); | |
048786d7 | 200 | bitmap_to_dump = ext2fs_block_bitmap_loc(current_fs, group_to_dump); |
da81e3fc | 201 | } |
da81e3fc | 202 | |
5faba3ac | 203 | if (!journal_fn && check_fs_open(argv[0])) |
0bed54fd | 204 | goto errout; |
5faba3ac TT |
205 | |
206 | if (journal_fn) { | |
da81e3fc TT |
207 | /* Set up to read journal from a regular file somewhere */ |
208 | journal_fd = open(journal_fn, O_RDONLY, 0); | |
209 | if (journal_fd < 0) { | |
210 | com_err(argv[0], errno, "while opening %s for logdump", | |
211 | journal_fn); | |
0bed54fd | 212 | goto errout; |
da81e3fc | 213 | } |
efc6f628 | 214 | |
da81e3fc TT |
215 | journal_source.where = JOURNAL_IS_EXTERNAL; |
216 | journal_source.fd = journal_fd; | |
f364093b | 217 | } else if ((journal_inum = es->s_journal_inum)) { |
a435ec34 TT |
218 | if (use_sb) { |
219 | if (es->s_jnl_backup_type != EXT3_JNL_BACKUP_BLOCKS) { | |
220 | com_err(argv[0], 0, | |
221 | "no journal backup in super block\n"); | |
0bed54fd | 222 | goto errout; |
a435ec34 TT |
223 | } |
224 | memset(&journal_inode, 0, sizeof(struct ext2_inode)); | |
efc6f628 | 225 | memcpy(&journal_inode.i_block[0], es->s_jnl_blocks, |
a435ec34 | 226 | EXT2_N_BLOCKS*4); |
931b58e1 | 227 | journal_inode.i_size_high = es->s_jnl_blocks[15]; |
a435ec34 TT |
228 | journal_inode.i_size = es->s_jnl_blocks[16]; |
229 | journal_inode.i_links_count = 1; | |
230 | journal_inode.i_mode = LINUX_S_IFREG | 0600; | |
231 | } else { | |
efc6f628 | 232 | if (debugfs_read_inode(journal_inum, &journal_inode, |
a435ec34 | 233 | argv[0])) |
0bed54fd | 234 | goto errout; |
a435ec34 | 235 | } |
efc6f628 | 236 | |
a435ec34 TT |
237 | retval = ext2fs_file_open2(current_fs, journal_inum, |
238 | &journal_inode, 0, &journal_file); | |
da81e3fc TT |
239 | if (retval) { |
240 | com_err(argv[0], retval, "while opening ext2 file"); | |
0bed54fd | 241 | goto errout; |
da81e3fc | 242 | } |
da81e3fc TT |
243 | journal_source.where = JOURNAL_IS_INTERNAL; |
244 | journal_source.file = journal_file; | |
f364093b TT |
245 | } else { |
246 | char uuid[37]; | |
efc6f628 | 247 | |
f364093b TT |
248 | uuid_unparse(es->s_journal_uuid, uuid); |
249 | journal_fn = blkid_get_devname(NULL, "UUID", uuid); | |
250 | if (!journal_fn) | |
251 | journal_fn = blkid_devno_to_devname(es->s_journal_dev); | |
252 | if (!journal_fn) { | |
253 | com_err(argv[0], 0, "filesystem has no journal"); | |
0bed54fd | 254 | goto errout; |
f364093b | 255 | } |
5faba3ac TT |
256 | journal_fd = open(journal_fn, O_RDONLY, 0); |
257 | if (journal_fd < 0) { | |
258 | com_err(argv[0], errno, "while opening %s for logdump", | |
259 | journal_fn); | |
260 | free(journal_fn); | |
0bed54fd | 261 | goto errout; |
5faba3ac TT |
262 | } |
263 | fprintf(out_file, "Using external journal found at %s\n", | |
264 | journal_fn); | |
265 | free(journal_fn); | |
266 | journal_source.where = JOURNAL_IS_EXTERNAL; | |
267 | journal_source.fd = journal_fd; | |
da81e3fc TT |
268 | } |
269 | ||
270 | dump_journal(argv[0], out_file, &journal_source); | |
271 | ||
272 | if (journal_source.where == JOURNAL_IS_INTERNAL) | |
273 | ext2fs_file_close(journal_file); | |
274 | else | |
275 | close(journal_fd); | |
276 | ||
0bed54fd | 277 | errout: |
dad0bab2 | 278 | if (out_file && (out_file != stdout)) |
da81e3fc TT |
279 | fclose(out_file); |
280 | ||
281 | return; | |
9b9a780f TT |
282 | |
283 | print_usage: | |
7600aa0f | 284 | fprintf(stderr, "%s: Usage: logdump [-acsOS] [-b<block>] [-i<filespec>]\n\t" |
9b9a780f | 285 | "[-f<journal_file>] [output_file]\n", argv[0]); |
da81e3fc TT |
286 | } |
287 | ||
288 | ||
efc6f628 | 289 | static int read_journal_block(const char *cmd, struct journal_source *source, |
5eca88c1 | 290 | ext2_loff_t offset, char *buf, unsigned int size) |
da81e3fc TT |
291 | { |
292 | int retval; | |
b34b94d5 | 293 | unsigned int got; |
efc6f628 | 294 | |
da81e3fc | 295 | if (source->where == JOURNAL_IS_EXTERNAL) { |
4bb0c043 TT |
296 | if (lseek(source->fd, offset, SEEK_SET) < 0) { |
297 | retval = errno; | |
b34b94d5 | 298 | goto seek_err; |
4bb0c043 TT |
299 | } |
300 | retval = read(source->fd, buf, size); | |
b34b94d5 | 301 | if (retval < 0) { |
5faba3ac | 302 | retval = errno; |
b34b94d5 TT |
303 | goto read_err; |
304 | } | |
305 | got = retval; | |
306 | retval = 0; | |
da81e3fc | 307 | } else { |
5eca88c1 TT |
308 | retval = ext2fs_file_llseek(source->file, offset, |
309 | EXT2_SEEK_SET, NULL); | |
da81e3fc | 310 | if (retval) { |
b34b94d5 | 311 | seek_err: |
da81e3fc TT |
312 | com_err(cmd, retval, "while seeking in reading journal"); |
313 | return retval; | |
314 | } | |
b34b94d5 TT |
315 | retval = ext2fs_file_read(source->file, buf, size, &got); |
316 | if (retval) { | |
317 | read_err: | |
318 | com_err(cmd, retval, "while reading journal"); | |
319 | return retval; | |
320 | } | |
da81e3fc | 321 | } |
b34b94d5 TT |
322 | if (got != size) { |
323 | com_err(cmd, 0, "short read (read %u, expected %u) " | |
324 | "while reading journal", got, size); | |
da81e3fc TT |
325 | retval = -1; |
326 | } | |
da81e3fc TT |
327 | return retval; |
328 | } | |
329 | ||
5e4f0709 | 330 | static const char *type_to_name(int btype) |
da81e3fc TT |
331 | { |
332 | switch (btype) { | |
8335d3c5 | 333 | case JBD2_DESCRIPTOR_BLOCK: |
da81e3fc | 334 | return "descriptor block"; |
8335d3c5 | 335 | case JBD2_COMMIT_BLOCK: |
da81e3fc | 336 | return "commit block"; |
8335d3c5 | 337 | case JBD2_SUPERBLOCK_V1: |
da81e3fc | 338 | return "V1 superblock"; |
8335d3c5 | 339 | case JBD2_SUPERBLOCK_V2: |
da81e3fc | 340 | return "V2 superblock"; |
8335d3c5 | 341 | case JBD2_REVOKE_BLOCK: |
da81e3fc | 342 | return "revoke table"; |
da81e3fc TT |
343 | } |
344 | return "unrecognised type"; | |
345 | } | |
346 | ||
347 | ||
efc6f628 | 348 | static void dump_journal(char *cmdname, FILE *out_file, |
da81e3fc TT |
349 | struct journal_source *source) |
350 | { | |
5faba3ac | 351 | struct ext2_super_block *sb; |
da81e3fc TT |
352 | char jsb_buffer[1024]; |
353 | char buf[8192]; | |
354 | journal_superblock_t *jsb; | |
54434927 | 355 | unsigned int blocksize = 1024; |
da81e3fc TT |
356 | int retval; |
357 | __u32 magic, sequence, blocktype; | |
358 | journal_header_t *header; | |
da81e3fc | 359 | tid_t transaction; |
5faba3ac | 360 | unsigned int blocknr = 0; |
8968289b | 361 | int fc_done; |
efc6f628 | 362 | |
5faba3ac | 363 | /* First, check to see if there's an ext2 superblock header */ |
b34b94d5 | 364 | retval = read_journal_block(cmdname, source, 0, buf, 2048); |
da81e3fc TT |
365 | if (retval) |
366 | return; | |
5faba3ac TT |
367 | |
368 | jsb = (journal_superblock_t *) buf; | |
369 | sb = (struct ext2_super_block *) (buf+1024); | |
2eae0930 | 370 | #ifdef WORDS_BIGENDIAN |
efc6f628 | 371 | if (sb->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC)) |
5faba3ac | 372 | ext2fs_swap_super(sb); |
ff63f268 | 373 | #endif |
efc6f628 | 374 | |
8335d3c5 | 375 | if ((be32_to_cpu(jsb->s_header.h_magic) != JBD2_MAGIC_NUMBER) && |
5faba3ac | 376 | (sb->s_magic == EXT2_SUPER_MAGIC) && |
4ee26699 | 377 | ext2fs_has_feature_journal_dev(sb)) { |
5faba3ac TT |
378 | blocksize = EXT2_BLOCK_SIZE(sb); |
379 | blocknr = (blocksize == 1024) ? 2 : 1; | |
4ea7bd04 | 380 | uuid_unparse(sb->s_uuid, jsb_buffer); |
5faba3ac TT |
381 | fprintf(out_file, "Ext2 superblock header found.\n"); |
382 | if (dump_all) { | |
383 | fprintf(out_file, "\tuuid=%s\n", jsb_buffer); | |
384 | fprintf(out_file, "\tblocksize=%d\n", blocksize); | |
5d38ef1d | 385 | fprintf(out_file, "\tjournal data size %lu\n", |
4efbac6f | 386 | (unsigned long) ext2fs_blocks_count(sb)); |
5faba3ac TT |
387 | } |
388 | } | |
efc6f628 | 389 | |
5faba3ac | 390 | /* Next, read the journal superblock */ |
5eca88c1 TT |
391 | retval = read_journal_block(cmdname, source, |
392 | ((ext2_loff_t) blocknr) * blocksize, | |
b34b94d5 | 393 | jsb_buffer, 1024); |
5faba3ac TT |
394 | if (retval) |
395 | return; | |
396 | ||
03a179a1 TT |
397 | if (dump_super) { |
398 | e2p_list_journal_super(out_file, jsb_buffer, | |
399 | current_fs->blocksize, 0); | |
400 | fputc('\n', out_file); | |
401 | } | |
402 | ||
da81e3fc | 403 | jsb = (journal_superblock_t *) jsb_buffer; |
8335d3c5 | 404 | if (be32_to_cpu(jsb->s_header.h_magic) != JBD2_MAGIC_NUMBER) { |
5faba3ac TT |
405 | fprintf(out_file, |
406 | "Journal superblock magic number invalid!\n"); | |
407 | return; | |
408 | } | |
da81e3fc TT |
409 | blocksize = be32_to_cpu(jsb->s_blocksize); |
410 | transaction = be32_to_cpu(jsb->s_sequence); | |
411 | blocknr = be32_to_cpu(jsb->s_start); | |
412 | ||
413 | fprintf(out_file, "Journal starts at block %u, transaction %u\n", | |
414 | blocknr, transaction); | |
415 | ||
46272d5a | 416 | if (!blocknr) { |
da81e3fc | 417 | /* Empty journal, nothing to do. */ |
46272d5a | 418 | if (!dump_old) |
8968289b | 419 | goto fc; |
46272d5a DW |
420 | else |
421 | blocknr = 1; | |
422 | } | |
efc6f628 | 423 | |
da81e3fc | 424 | while (1) { |
efc6f628 | 425 | retval = read_journal_block(cmdname, source, |
5eca88c1 TT |
426 | ((ext2_loff_t) blocknr) * blocksize, |
427 | buf, blocksize); | |
b34b94d5 | 428 | if (retval) |
8968289b | 429 | break; |
efc6f628 | 430 | |
da81e3fc TT |
431 | header = (journal_header_t *) buf; |
432 | ||
433 | magic = be32_to_cpu(header->h_magic); | |
434 | sequence = be32_to_cpu(header->h_sequence); | |
435 | blocktype = be32_to_cpu(header->h_blocktype); | |
efc6f628 | 436 | |
8335d3c5 | 437 | if (magic != JBD2_MAGIC_NUMBER) { |
da81e3fc TT |
438 | fprintf (out_file, "No magic number at block %u: " |
439 | "end of journal.\n", blocknr); | |
8968289b | 440 | break; |
da81e3fc | 441 | } |
efc6f628 | 442 | |
da81e3fc TT |
443 | if (sequence != transaction) { |
444 | fprintf (out_file, "Found sequence %u (not %u) at " | |
efc6f628 | 445 | "block %u: end of journal.\n", |
da81e3fc | 446 | sequence, transaction, blocknr); |
46272d5a | 447 | if (!dump_old) |
8968289b | 448 | break; |
da81e3fc TT |
449 | } |
450 | ||
451 | if (dump_descriptors) { | |
452 | fprintf (out_file, "Found expected sequence %u, " | |
453 | "type %u (%s) at block %u\n", | |
efc6f628 | 454 | sequence, blocktype, |
da81e3fc TT |
455 | type_to_name(blocktype), blocknr); |
456 | } | |
efc6f628 | 457 | |
da81e3fc | 458 | switch (blocktype) { |
8335d3c5 | 459 | case JBD2_DESCRIPTOR_BLOCK: |
efc6f628 | 460 | dump_descriptor_block(out_file, source, buf, jsb, |
da81e3fc TT |
461 | &blocknr, blocksize, |
462 | transaction); | |
463 | continue; | |
464 | ||
8335d3c5 | 465 | case JBD2_COMMIT_BLOCK: |
da81e3fc TT |
466 | transaction++; |
467 | blocknr++; | |
468 | WRAP(jsb, blocknr); | |
469 | continue; | |
efc6f628 | 470 | |
8335d3c5 | 471 | case JBD2_REVOKE_BLOCK: |
da81e3fc | 472 | dump_revoke_block(out_file, buf, jsb, |
efc6f628 | 473 | blocknr, blocksize, |
da81e3fc TT |
474 | transaction); |
475 | blocknr++; | |
476 | WRAP(jsb, blocknr); | |
477 | continue; | |
478 | ||
479 | default: | |
480 | fprintf (out_file, "Unexpected block type %u at " | |
481 | "block %u.\n", blocktype, blocknr); | |
8968289b | 482 | break; |
da81e3fc TT |
483 | } |
484 | } | |
8968289b HS |
485 | |
486 | fc: | |
487 | blocknr = be32_to_cpu(jsb->s_maxlen) - jbd2_journal_get_num_fc_blks(jsb) + 1; | |
488 | while (blocknr <= be32_to_cpu(jsb->s_maxlen)) { | |
489 | retval = read_journal_block(cmdname, source, | |
490 | ((ext2_loff_t) blocknr) * blocksize, | |
491 | buf, blocksize); | |
492 | if (retval) | |
493 | return; | |
494 | ||
495 | dump_fc_block(out_file, buf, blocksize, transaction, &fc_done, | |
496 | dump_old); | |
497 | if (!dump_old && fc_done) | |
498 | break; | |
499 | blocknr++; | |
500 | } | |
da81e3fc TT |
501 | } |
502 | ||
df0b907e TT |
503 | static inline size_t journal_super_tag_bytes(journal_superblock_t *jsb) |
504 | { | |
505 | size_t sz; | |
506 | ||
8335d3c5 | 507 | if (JSB_HAS_INCOMPAT_FEATURE(jsb, JBD2_FEATURE_INCOMPAT_CSUM_V3)) |
df0b907e TT |
508 | return sizeof(journal_block_tag3_t); |
509 | ||
510 | sz = sizeof(journal_block_tag_t); | |
511 | ||
8335d3c5 | 512 | if (JSB_HAS_INCOMPAT_FEATURE(jsb, JBD2_FEATURE_INCOMPAT_CSUM_V2)) |
df0b907e TT |
513 | sz += sizeof(__u16); |
514 | ||
8335d3c5 | 515 | if (JSB_HAS_INCOMPAT_FEATURE(jsb, JBD2_FEATURE_INCOMPAT_64BIT)) |
df0b907e TT |
516 | return sz; |
517 | ||
518 | return sz - sizeof(__u32); | |
519 | } | |
da81e3fc | 520 | |
8968289b HS |
521 | static void dump_fc_block(FILE *out_file, char *buf, int blocksize, |
522 | int transaction, int *fc_done, int dump_old) | |
523 | { | |
524 | struct ext4_fc_tl *tl; | |
525 | struct ext4_fc_head *head; | |
526 | struct ext4_fc_add_range *add_range; | |
527 | struct ext4_fc_del_range *del_range; | |
528 | struct ext4_fc_dentry_info *dentry_info; | |
529 | struct ext4_fc_tail *tail; | |
530 | struct ext3_extent *ex; | |
531 | ||
532 | *fc_done = 0; | |
533 | fc_for_each_tl(buf, buf + blocksize, tl) { | |
534 | switch (le16_to_cpu(tl->fc_tag)) { | |
535 | case EXT4_FC_TAG_ADD_RANGE: | |
536 | add_range = | |
537 | (struct ext4_fc_add_range *)ext4_fc_tag_val(tl); | |
538 | ex = (struct ext3_extent *)add_range->fc_ex; | |
539 | fprintf(out_file, | |
f41c7da2 | 540 | "tag %s, inode %d, lblk %u, pblk %llu, len %lu\n", |
8968289b HS |
541 | tag2str(tl->fc_tag), |
542 | le32_to_cpu(add_range->fc_ino), | |
543 | le32_to_cpu(ex->ee_block), | |
544 | le32_to_cpu(ex->ee_start) + | |
33b9a60c | 545 | (((unsigned long long) le16_to_cpu(ex->ee_start_hi)) << 32), |
8968289b HS |
546 | le16_to_cpu(ex->ee_len) > EXT_INIT_MAX_LEN ? |
547 | le16_to_cpu(ex->ee_len) - EXT_INIT_MAX_LEN : | |
548 | le16_to_cpu(ex->ee_len)); | |
549 | break; | |
550 | case EXT4_FC_TAG_DEL_RANGE: | |
551 | del_range = | |
552 | (struct ext4_fc_del_range *)ext4_fc_tag_val(tl); | |
553 | fprintf(out_file, "tag %s, inode %d, lblk %d, len %d\n", | |
554 | tag2str(tl->fc_tag), | |
555 | le32_to_cpu(del_range->fc_ino), | |
556 | le32_to_cpu(del_range->fc_lblk), | |
557 | le32_to_cpu(del_range->fc_len)); | |
558 | break; | |
559 | case EXT4_FC_TAG_LINK: | |
560 | case EXT4_FC_TAG_UNLINK: | |
561 | case EXT4_FC_TAG_CREAT: | |
562 | dentry_info = | |
563 | (struct ext4_fc_dentry_info *) | |
564 | ext4_fc_tag_val(tl); | |
565 | fprintf(out_file, | |
566 | "tag %s, parent %d, ino %d, name \"%s\"\n", | |
567 | tag2str(tl->fc_tag), | |
568 | le32_to_cpu(dentry_info->fc_parent_ino), | |
569 | le32_to_cpu(dentry_info->fc_ino), | |
570 | dentry_info->fc_dname); | |
571 | break; | |
572 | case EXT4_FC_TAG_INODE: | |
573 | fprintf(out_file, "tag %s, inode %d\n", | |
574 | tag2str(tl->fc_tag), | |
575 | le32_to_cpu(((struct ext4_fc_inode *) | |
576 | ext4_fc_tag_val(tl))->fc_ino)); | |
577 | break; | |
578 | case EXT4_FC_TAG_PAD: | |
579 | fprintf(out_file, "tag %s\n", tag2str(tl->fc_tag)); | |
580 | break; | |
581 | case EXT4_FC_TAG_TAIL: | |
582 | tail = (struct ext4_fc_tail *)ext4_fc_tag_val(tl); | |
583 | fprintf(out_file, "tag %s, tid %d\n", | |
584 | tag2str(tl->fc_tag), | |
585 | le32_to_cpu(tail->fc_tid)); | |
586 | if (!dump_old && | |
587 | le32_to_cpu(tail->fc_tid) < transaction) { | |
588 | *fc_done = 1; | |
589 | return; | |
590 | } | |
591 | break; | |
592 | case EXT4_FC_TAG_HEAD: | |
593 | fprintf(out_file, "\n*** Fast Commit Area ***\n"); | |
594 | head = (struct ext4_fc_head *)ext4_fc_tag_val(tl); | |
595 | fprintf(out_file, "tag %s, features 0x%x, tid %d\n", | |
596 | tag2str(tl->fc_tag), | |
597 | le32_to_cpu(head->fc_features), | |
598 | le32_to_cpu(head->fc_tid)); | |
599 | if (!dump_old && | |
600 | le32_to_cpu(head->fc_tid) < transaction) { | |
601 | *fc_done = 1; | |
602 | return; | |
603 | } | |
604 | break; | |
605 | default: | |
606 | *fc_done = 1; | |
607 | break; | |
608 | } | |
609 | } | |
610 | } | |
611 | ||
efc6f628 TT |
612 | static void dump_descriptor_block(FILE *out_file, |
613 | struct journal_source *source, | |
da81e3fc | 614 | char *buf, |
efc6f628 | 615 | journal_superblock_t *jsb, |
da81e3fc TT |
616 | unsigned int *blockp, int blocksize, |
617 | tid_t transaction) | |
618 | { | |
38d5adf3 | 619 | int offset, tag_size, csum_size = 0; |
da81e3fc TT |
620 | char *tagp; |
621 | journal_block_tag_t *tag; | |
622 | unsigned int blocknr; | |
623 | __u32 tag_block; | |
624 | __u32 tag_flags; | |
efc6f628 | 625 | |
38d5adf3 | 626 | tag_size = journal_super_tag_bytes(jsb); |
da81e3fc TT |
627 | offset = sizeof(journal_header_t); |
628 | blocknr = *blockp; | |
629 | ||
8335d3c5 TT |
630 | if (JSB_HAS_INCOMPAT_FEATURE(jsb, JBD2_FEATURE_INCOMPAT_CSUM_V3) || |
631 | JSB_HAS_INCOMPAT_FEATURE(jsb, JBD2_FEATURE_INCOMPAT_CSUM_V2)) | |
632 | csum_size = sizeof(struct jbd2_journal_block_tail); | |
38d5adf3 | 633 | |
efc6f628 | 634 | if (dump_all) |
da81e3fc TT |
635 | fprintf(out_file, "Dumping descriptor block, sequence %u, at " |
636 | "block %u:\n", transaction, blocknr); | |
efc6f628 | 637 | |
da81e3fc TT |
638 | ++blocknr; |
639 | WRAP(jsb, blocknr); | |
efc6f628 | 640 | |
da81e3fc | 641 | do { |
efc6f628 | 642 | /* Work out the location of the current tag, and skip to |
da81e3fc TT |
643 | * the next one... */ |
644 | tagp = &buf[offset]; | |
645 | tag = (journal_block_tag_t *) tagp; | |
98446d73 | 646 | offset += tag_size; |
da81e3fc TT |
647 | |
648 | /* ... and if we have gone too far, then we've reached the | |
649 | end of this block. */ | |
38d5adf3 | 650 | if (offset > blocksize - csum_size) |
da81e3fc | 651 | break; |
efc6f628 | 652 | |
da81e3fc | 653 | tag_block = be32_to_cpu(tag->t_blocknr); |
2556373a | 654 | tag_flags = be16_to_cpu(tag->t_flags); |
da81e3fc | 655 | |
8335d3c5 | 656 | if (!(tag_flags & JBD2_FLAG_SAME_UUID)) |
da81e3fc TT |
657 | offset += 16; |
658 | ||
efc6f628 | 659 | dump_metadata_block(out_file, source, jsb, |
98446d73 | 660 | blocknr, tag_block, tag_flags, blocksize, |
da81e3fc TT |
661 | transaction); |
662 | ||
663 | ++blocknr; | |
664 | WRAP(jsb, blocknr); | |
efc6f628 | 665 | |
8335d3c5 | 666 | } while (!(tag_flags & JBD2_FLAG_LAST_TAG)); |
efc6f628 | 667 | |
da81e3fc TT |
668 | *blockp = blocknr; |
669 | } | |
670 | ||
671 | ||
672 | static void dump_revoke_block(FILE *out_file, char *buf, | |
efc6f628 TT |
673 | journal_superblock_t *jsb EXT2FS_ATTR((unused)), |
674 | unsigned int blocknr, | |
54434927 TT |
675 | int blocksize EXT2FS_ATTR((unused)), |
676 | tid_t transaction) | |
da81e3fc TT |
677 | { |
678 | int offset, max; | |
8335d3c5 | 679 | jbd2_journal_revoke_header_t *header; |
a1ff15f8 DW |
680 | unsigned long long rblock; |
681 | int tag_size = sizeof(__u32); | |
efc6f628 TT |
682 | |
683 | if (dump_all) | |
da81e3fc TT |
684 | fprintf(out_file, "Dumping revoke block, sequence %u, at " |
685 | "block %u:\n", transaction, blocknr); | |
efc6f628 | 686 | |
8335d3c5 | 687 | if (be32_to_cpu(jsb->s_feature_incompat) & JBD2_FEATURE_INCOMPAT_64BIT) |
a1ff15f8 DW |
688 | tag_size = sizeof(__u64); |
689 | ||
8335d3c5 TT |
690 | header = (jbd2_journal_revoke_header_t *) buf; |
691 | offset = sizeof(jbd2_journal_revoke_header_t); | |
da81e3fc TT |
692 | max = be32_to_cpu(header->r_count); |
693 | ||
694 | while (offset < max) { | |
a1ff15f8 DW |
695 | if (tag_size == sizeof(__u32)) { |
696 | __u32 *entry = (__u32 *) (buf + offset); | |
697 | rblock = be32_to_cpu(*entry); | |
698 | } else { | |
699 | __u64 *entry = (__u64 *) (buf + offset); | |
700 | rblock = ext2fs_be64_to_cpu(*entry); | |
701 | } | |
da81e3fc | 702 | if (dump_all || rblock == block_to_dump) { |
33b9a60c TT |
703 | fprintf(out_file, " Revoke FS block %llu", |
704 | (unsigned long long) rblock); | |
da81e3fc TT |
705 | if (dump_all) |
706 | fprintf(out_file, "\n"); | |
707 | else | |
708 | fprintf(out_file," at block %u, sequence %u\n", | |
709 | blocknr, transaction); | |
710 | } | |
a1ff15f8 | 711 | offset += tag_size; |
da81e3fc TT |
712 | } |
713 | } | |
714 | ||
715 | ||
716 | static void show_extent(FILE *out_file, int start_extent, int end_extent, | |
717 | __u32 first_block) | |
718 | { | |
719 | if (start_extent >= 0 && first_block != 0) | |
efc6f628 | 720 | fprintf(out_file, "(%d+%u): %u ", |
da81e3fc TT |
721 | start_extent, end_extent-start_extent, first_block); |
722 | } | |
723 | ||
5e4f0709 | 724 | static void show_indirect(FILE *out_file, const char *name, __u32 where) |
da81e3fc TT |
725 | { |
726 | if (where) | |
727 | fprintf(out_file, "(%s): %u ", name, where); | |
728 | } | |
729 | ||
730 | ||
731 | static void dump_metadata_block(FILE *out_file, struct journal_source *source, | |
54434927 | 732 | journal_superblock_t *jsb EXT2FS_ATTR((unused)), |
efc6f628 TT |
733 | unsigned int log_blocknr, |
734 | unsigned int fs_blocknr, | |
98446d73 | 735 | unsigned int log_tag_flags, |
da81e3fc TT |
736 | int blocksize, |
737 | tid_t transaction) | |
738 | { | |
5e4f0709 TT |
739 | int retval; |
740 | char buf[8192]; | |
efc6f628 | 741 | |
da81e3fc TT |
742 | if (!(dump_all |
743 | || (fs_blocknr == block_to_dump) | |
744 | || (fs_blocknr == inode_block_to_dump) | |
745 | || (fs_blocknr == bitmap_to_dump))) | |
746 | return; | |
efc6f628 | 747 | |
da81e3fc | 748 | fprintf(out_file, " FS block %u logged at ", fs_blocknr); |
efc6f628 | 749 | if (!dump_all) |
da81e3fc | 750 | fprintf(out_file, "sequence %u, ", transaction); |
98446d73 TT |
751 | fprintf(out_file, "journal block %u (flags 0x%x)\n", log_blocknr, |
752 | log_tag_flags); | |
efc6f628 | 753 | |
da81e3fc | 754 | /* There are two major special cases to parse: |
efc6f628 | 755 | * |
da81e3fc TT |
756 | * If this block is a block |
757 | * bitmap block, we need to give it special treatment so that we | |
758 | * can log any allocates and deallocates which affect the | |
efc6f628 TT |
759 | * block_to_dump query block. |
760 | * | |
da81e3fc TT |
761 | * If the block is an inode block for the inode being searched |
762 | * for, then we need to dump the contents of that inode | |
efc6f628 | 763 | * structure symbolically. |
da81e3fc | 764 | */ |
efc6f628 | 765 | |
da81e3fc TT |
766 | if (!(dump_contents && dump_all) |
767 | && fs_blocknr != block_to_dump | |
efc6f628 | 768 | && fs_blocknr != bitmap_to_dump |
da81e3fc TT |
769 | && fs_blocknr != inode_block_to_dump) |
770 | return; | |
efc6f628 TT |
771 | |
772 | retval = read_journal_block("logdump", source, | |
5eca88c1 | 773 | ((ext2_loff_t) log_blocknr) * blocksize, |
b34b94d5 | 774 | buf, blocksize); |
da81e3fc TT |
775 | if (retval) |
776 | return; | |
efc6f628 | 777 | |
da81e3fc TT |
778 | if (fs_blocknr == bitmap_to_dump) { |
779 | struct ext2_super_block *super; | |
780 | int offset; | |
efc6f628 | 781 | |
da81e3fc | 782 | super = current_fs->super; |
de2c4778 | 783 | offset = ((block_to_dump - super->s_first_data_block) % |
da81e3fc | 784 | super->s_blocks_per_group); |
efc6f628 | 785 | |
4dbfd79d | 786 | fprintf(out_file, " (block bitmap for block %llu: " |
efc6f628 | 787 | "block is %s)\n", |
33b9a60c | 788 | (unsigned long long) block_to_dump, |
da81e3fc TT |
789 | ext2fs_test_bit(offset, buf) ? "SET" : "CLEAR"); |
790 | } | |
efc6f628 | 791 | |
da81e3fc TT |
792 | if (fs_blocknr == inode_block_to_dump) { |
793 | struct ext2_inode *inode; | |
794 | int first, prev, this, start_extent, i; | |
efc6f628 | 795 | |
da81e3fc TT |
796 | fprintf(out_file, " (inode block for inode %u):\n", |
797 | inode_to_dump); | |
efc6f628 | 798 | |
da81e3fc TT |
799 | inode = (struct ext2_inode *) (buf + inode_offset_to_dump); |
800 | internal_dump_inode(out_file, " ", inode_to_dump, inode, 0); | |
efc6f628 | 801 | |
da81e3fc TT |
802 | /* Dump out the direct/indirect blocks here: |
803 | * internal_dump_inode can only dump them from the main | |
804 | * on-disk inode, not from the journaled copy of the | |
805 | * inode. */ | |
efc6f628 | 806 | |
da81e3fc | 807 | fprintf (out_file, " Blocks: "); |
5e4f0709 | 808 | first = prev = start_extent = -1; |
da81e3fc TT |
809 | |
810 | for (i=0; i<EXT2_NDIR_BLOCKS; i++) { | |
811 | this = inode->i_block[i]; | |
812 | if (start_extent >= 0 && this == prev+1) { | |
813 | prev = this; | |
814 | continue; | |
815 | } else { | |
816 | show_extent(out_file, start_extent, i, first); | |
817 | start_extent = i; | |
818 | first = prev = this; | |
819 | } | |
820 | } | |
821 | show_extent(out_file, start_extent, i, first); | |
822 | show_indirect(out_file, "IND", inode->i_block[i++]); | |
823 | show_indirect(out_file, "DIND", inode->i_block[i++]); | |
824 | show_indirect(out_file, "TIND", inode->i_block[i++]); | |
efc6f628 | 825 | |
da81e3fc TT |
826 | fprintf(out_file, "\n"); |
827 | } | |
828 | ||
829 | if (dump_contents) | |
830 | do_hexdump(out_file, buf, blocksize); | |
efc6f628 | 831 | |
da81e3fc TT |
832 | } |
833 | ||
834 | static void do_hexdump (FILE *out_file, char *buf, int blocksize) | |
835 | { | |
836 | int i,j; | |
837 | int *intp; | |
838 | char *charp; | |
839 | unsigned char c; | |
efc6f628 | 840 | |
da81e3fc TT |
841 | intp = (int *) buf; |
842 | charp = (char *) buf; | |
efc6f628 | 843 | |
da81e3fc TT |
844 | for (i=0; i<blocksize; i+=16) { |
845 | fprintf(out_file, " %04x: ", i); | |
846 | for (j=0; j<16; j+=4) | |
847 | fprintf(out_file, "%08x ", *intp++); | |
848 | for (j=0; j<16; j++) { | |
849 | c = *charp++; | |
850 | if (c < ' ' || c >= 127) | |
851 | c = '.'; | |
852 | fprintf(out_file, "%c", c); | |
853 | } | |
854 | fprintf(out_file, "\n"); | |
855 | } | |
856 | } | |
857 |