/* * calc_chksums calculates the checksums for the blocks described in the * descriptor block. */ static int calc_chksums(journal_t *journal, struct buffer_head *bh, unsigned long long *next_log_block, __u32 *crc32_sum) { int i, num_blks, err; unsigned long long io_block; struct buffer_head *obh; num_blks = count_tags(journal, bh); /* Calculate checksum of the descriptor block. */ *crc32_sum = crc32_be(*crc32_sum, (void *)bh->b_data, bh->b_size); for (i = 0; i < num_blks; i++) { io_block = (*next_log_block)++; wrap(journal, *next_log_block); err = jread(&obh, journal, io_block); if (err) { printk(KERN_ERR "JBD: IO error %d recovering block " "%llu in log\n", err, io_block); return 1; } else { *crc32_sum = crc32_be(*crc32_sum, (void *)obh->b_data, obh->b_size); } brelse(obh); } return 0; }
test_exe_head(int file, Exe_head *exe) { if (jread(file, exe, sizeof(*exe)) != sizeof(*exe) ) return(0); if (exe->M != 'M' || exe->Z != 'Z' || exe->one != 1) return(0); return(1); }
/* fread() wrapper */ size_t jfread(void *ptr, size_t size, size_t nmemb, struct jfs *stream) { int rv; rv = jread(stream, ptr, size * nmemb); if (rv <= 0) return 0; return rv / size; }
static int do_one_pass(journal_t *journal, struct recovery_info *info, enum passtype pass) { unsigned int first_commit_ID, next_commit_ID; unsigned long next_log_block; int err, success = 0; journal_superblock_t * sb; journal_header_t * tmp; struct buffer_head * bh; unsigned int sequence; int blocktype; /* Precompute the maximum metadata descriptors in a descriptor block */ int MAX_BLOCKS_PER_DESC; MAX_BLOCKS_PER_DESC = ((journal->j_blocksize-sizeof(journal_header_t)) / sizeof(journal_block_tag_t)); /* * First thing is to establish what we expect to find in the log * (in terms of transaction IDs), and where (in terms of log * block offsets): query the superblock. */ sb = journal->j_superblock; next_commit_ID = be32_to_cpu(sb->s_sequence); next_log_block = be32_to_cpu(sb->s_start); first_commit_ID = next_commit_ID; if (pass == PASS_SCAN) info->start_transaction = first_commit_ID; jbd_debug(1, "Starting recovery pass %d\n", pass); /* * Now we walk through the log, transaction by transaction, * making sure that each transaction has a commit block in the * expected place. Each complete transaction gets replayed back * into the main filesystem. */ while (1) { int flags; char * tagp; journal_block_tag_t * tag; struct buffer_head * obh; struct buffer_head * nbh; cond_resched(); /* We're under lock_kernel() */ /* If we already know where to stop the log traversal, * check right now that we haven't gone past the end of * the log. */ if (pass != PASS_SCAN) if (tid_geq(next_commit_ID, info->end_transaction)) break; jbd_debug(2, "Scanning for sequence ID %u at %lu/%lu\n", next_commit_ID, next_log_block, journal->j_last); /* Skip over each chunk of the transaction looking * either the next descriptor block or the final commit * record. */ jbd_debug(3, "JBD: checking block %ld\n", next_log_block); err = jread(&bh, journal, next_log_block); if (err) goto failed; next_log_block++; wrap(journal, next_log_block); /* What kind of buffer is it? * * If it is a descriptor block, check that it has the * expected sequence number. Otherwise, we're all done * here. */ tmp = (journal_header_t *)bh->b_data; if (tmp->h_magic != cpu_to_be32(JFS_MAGIC_NUMBER)) { brelse(bh); break; } blocktype = be32_to_cpu(tmp->h_blocktype); sequence = be32_to_cpu(tmp->h_sequence); jbd_debug(3, "Found magic %d, sequence %d\n", blocktype, sequence); if (sequence != next_commit_ID) { brelse(bh); break; } /* OK, we have a valid descriptor block which matches * all of the sequence number checks. What are we going * to do with it? That depends on the pass... */ switch(blocktype) { case JFS_DESCRIPTOR_BLOCK: /* If it is a valid descriptor block, replay it * in pass REPLAY; otherwise, just skip over the * blocks it describes. */ if (pass != PASS_REPLAY) { next_log_block += count_tags(bh, journal->j_blocksize); wrap(journal, next_log_block); brelse(bh); continue; } /* A descriptor block: we can now write all of * the data blocks. Yay, useful work is finally * getting done here! */ tagp = &bh->b_data[sizeof(journal_header_t)]; while ((tagp - bh->b_data +sizeof(journal_block_tag_t)) <= journal->j_blocksize) { unsigned long io_block; tag = (journal_block_tag_t *) tagp; flags = be32_to_cpu(tag->t_flags); io_block = next_log_block++; wrap(journal, next_log_block); err = jread(&obh, journal, io_block); if (err) { /* Recover what we can, but * report failure at the end. */ success = err; printk (KERN_ERR "JBD: IO error %d recovering " "block %ld in log\n", err, io_block); } else { unsigned long blocknr; J_ASSERT(obh != NULL); blocknr = be32_to_cpu(tag->t_blocknr); /* If the block has been * revoked, then we're all done * here. */ if (journal_test_revoke (journal, blocknr, next_commit_ID)) { brelse(obh); ++info->nr_revoke_hits; goto skip_write; } /* Find a buffer for the new * data being restored */ nbh = __getblk(journal->j_fs_dev, blocknr, journal->j_blocksize); if (nbh == NULL) { printk(KERN_ERR "JBD: Out of memory " "during recovery.\n"); err = -ENOMEM; brelse(bh); brelse(obh); goto failed; } lock_buffer(nbh); memcpy(nbh->b_data, obh->b_data, journal->j_blocksize); if (flags & JFS_FLAG_ESCAPE) { *((__be32 *)bh->b_data) = cpu_to_be32(JFS_MAGIC_NUMBER); } BUFFER_TRACE(nbh, "marking dirty"); set_buffer_uptodate(nbh); mark_buffer_dirty(nbh); BUFFER_TRACE(nbh, "marking uptodate"); ++info->nr_replays; /* ll_rw_block(WRITE, 1, &nbh); */ unlock_buffer(nbh); brelse(obh); brelse(nbh); } skip_write: tagp += sizeof(journal_block_tag_t); if (!(flags & JFS_FLAG_SAME_UUID)) tagp += 16; if (flags & JFS_FLAG_LAST_TAG) break; } brelse(bh); continue; case JFS_COMMIT_BLOCK: /* Found an expected commit block: not much to * do other than move on to the next sequence * number. */ brelse(bh); next_commit_ID++; continue; case JFS_REVOKE_BLOCK: /* If we aren't in the REVOKE pass, then we can * just skip over this block. */ if (pass != PASS_REVOKE) { brelse(bh); continue; } err = scan_revoke_records(journal, bh, next_commit_ID, info); brelse(bh); if (err) goto failed; continue; default: jbd_debug(3, "Unrecognised magic %d, end of scan.\n", blocktype); goto done; } } done: /* * We broke out of the log scan loop: either we came to the * known end of the log or we found an unexpected block in the * log. If the latter happened, then we know that the "current" * transaction marks the end of the valid log. */ if (pass == PASS_SCAN) info->end_transaction = next_commit_ID; else { /* It's really bad news if different passes end up at * different places (but possible due to IO errors). */ if (info->end_transaction != next_commit_ID) { printk (KERN_ERR "JBD: recovery pass %d ended at " "transaction %u, expected %u\n", pass, next_commit_ID, info->end_transaction); if (!success) success = -EIO; } } return success; failed: return err; }
static int do_one_pass(journal_t *journal, struct recovery_info *info, enum passtype pass) { unsigned int first_commit_ID, next_commit_ID; unsigned long long next_log_block; int err, success = 0; journal_superblock_t * sb; journal_header_t * tmp; struct buffer_head * bh; unsigned int sequence; int blocktype; int tag_bytes = journal_tag_bytes(journal); __u32 crc32_sum = ~0; /* Transactional Checksums */ /* Precompute the maximum metadata descriptors in a descriptor block */ int MAX_BLOCKS_PER_DESC; MAX_BLOCKS_PER_DESC = ((journal->j_blocksize-sizeof(journal_header_t)) / tag_bytes); /* * First thing is to establish what we expect to find in the log * (in terms of transaction IDs), and where (in terms of log * block offsets): query the superblock. */ sb = journal->j_superblock; next_commit_ID = be32_to_cpu(sb->s_sequence); next_log_block = be32_to_cpu(sb->s_start); first_commit_ID = next_commit_ID; if (pass == PASS_SCAN) info->start_transaction = first_commit_ID; jbd_debug(1, "Starting recovery pass %d\n", pass); /* * Now we walk through the log, transaction by transaction, * making sure that each transaction has a commit block in the * expected place. Each complete transaction gets replayed back * into the main filesystem. */ while (1) { int flags; char * tagp; journal_block_tag_t * tag; struct buffer_head * obh; struct buffer_head * nbh; cond_resched(); /* If we already know where to stop the log traversal, * check right now that we haven't gone past the end of * the log. */ if (pass != PASS_SCAN) if (tid_geq(next_commit_ID, info->end_transaction)) break; jbd_debug(2, "Scanning for sequence ID %u at %lu/%lu\n", next_commit_ID, next_log_block, journal->j_last); /* Skip over each chunk of the transaction looking * either the next descriptor block or the final commit * record. */ jbd_debug(3, "JBD: checking block %ld\n", next_log_block); err = jread(&bh, journal, next_log_block); if (err) goto failed; next_log_block++; wrap(journal, next_log_block); /* What kind of buffer is it? * * If it is a descriptor block, check that it has the * expected sequence number. Otherwise, we're all done * here. */ tmp = (journal_header_t *)bh->b_data; if (tmp->h_magic != cpu_to_be32(JFS_MAGIC_NUMBER)) { brelse(bh); break; } blocktype = be32_to_cpu(tmp->h_blocktype); sequence = be32_to_cpu(tmp->h_sequence); jbd_debug(3, "Found magic %d, sequence %d\n", blocktype, sequence); if (sequence != next_commit_ID) { brelse(bh); break; } /* OK, we have a valid descriptor block which matches * all of the sequence number checks. What are we going * to do with it? That depends on the pass... */ switch(blocktype) { case JFS_DESCRIPTOR_BLOCK: /* If it is a valid descriptor block, replay it * in pass REPLAY; if journal_checksums enabled, then * calculate checksums in PASS_SCAN, otherwise, * just skip over the blocks it describes. */ if (pass != PASS_REPLAY) { if (pass == PASS_SCAN && JFS_HAS_COMPAT_FEATURE(journal, JFS_FEATURE_COMPAT_CHECKSUM) && !info->end_transaction) { if (calc_chksums(journal, bh, &next_log_block, &crc32_sum)) { brelse(bh); break; } brelse(bh); continue; } next_log_block += count_tags(journal, bh); wrap(journal, next_log_block); brelse(bh); continue; } /* A descriptor block: we can now write all of * the data blocks. Yay, useful work is finally * getting done here! */ tagp = &bh->b_data[sizeof(journal_header_t)]; while ((tagp - bh->b_data + tag_bytes) <= journal->j_blocksize) { unsigned long long io_block; tag = (journal_block_tag_t *) tagp; flags = be32_to_cpu(tag->t_flags); io_block = next_log_block++; wrap(journal, next_log_block); err = jread(&obh, journal, io_block); if (err) { /* Recover what we can, but * report failure at the end. */ success = err; printk (KERN_ERR "JBD: IO error %d recovering " "block %llu in log\n", err, io_block); } else { unsigned long long blocknr; J_ASSERT(obh != NULL); blocknr = read_tag_block(tag_bytes, tag); /* If the block has been * revoked, then we're all done * here. */ if (journal_test_revoke (journal, blocknr, next_commit_ID)) { brelse(obh); ++info->nr_revoke_hits; goto skip_write; } /* Find a buffer for the new * data being restored */ nbh = __getblk(journal->j_fs_dev, blocknr, journal->j_blocksize); if (nbh == NULL) { printk(KERN_ERR "JBD: Out of memory " "during recovery.\n"); err = -ENOMEM; brelse(bh); brelse(obh); goto failed; } lock_buffer(nbh); memcpy(nbh->b_data, obh->b_data, journal->j_blocksize); if (flags & JFS_FLAG_ESCAPE) { journal_header_t *header; header = (journal_header_t *) &nbh->b_data[0]; header->h_magic = cpu_to_be32(JFS_MAGIC_NUMBER); } BUFFER_TRACE(nbh, "marking dirty"); set_buffer_uptodate(nbh); mark_buffer_dirty(nbh); BUFFER_TRACE(nbh, "marking uptodate"); ++info->nr_replays; /* ll_rw_block(WRITE, 1, &nbh); */ unlock_buffer(nbh); brelse(obh); brelse(nbh); } skip_write: tagp += tag_bytes; if (!(flags & JFS_FLAG_SAME_UUID)) tagp += 16; if (flags & JFS_FLAG_LAST_TAG) break; } brelse(bh); continue; case JFS_COMMIT_BLOCK: jbd_debug(3, "Commit block for #%u found\n", next_commit_ID); /* How to differentiate between interrupted commit * and journal corruption ? * * {nth transaction} * Checksum Verification Failed * | * ____________________ * | | * async_commit sync_commit * | | * | GO TO NEXT "Journal Corruption" * | TRANSACTION * | * {(n+1)th transanction} * | * _______|______________ * | | * Commit block found Commit block not found * | | * "Journal Corruption" | * _____________|_________ * | | * nth trans corrupt OR nth trans * and (n+1)th interrupted interrupted * before commit block * could reach the disk. * (Cannot find the difference in above * mentioned conditions. Hence assume * "Interrupted Commit".) */ /* Found an expected commit block: if checksums * are present verify them in PASS_SCAN; else not * much to do other than move on to the next sequence * number. */ if (pass == PASS_SCAN && JFS_HAS_COMPAT_FEATURE(journal, JFS_FEATURE_COMPAT_CHECKSUM)) { int chksum_err, chksum_seen; struct commit_header *cbh = (struct commit_header *)bh->b_data; unsigned found_chksum = be32_to_cpu(cbh->h_chksum[0]); chksum_err = chksum_seen = 0; jbd_debug(3, "Checksums %x %x\n", crc32_sum, found_chksum); if (info->end_transaction) { journal->j_failed_commit = info->end_transaction; brelse(bh); break; } if (crc32_sum == found_chksum && cbh->h_chksum_type == JBD2_CRC32_CHKSUM && cbh->h_chksum_size == JBD2_CRC32_CHKSUM_SIZE) chksum_seen = 1; else if (!(cbh->h_chksum_type == 0 && cbh->h_chksum_size == 0 && found_chksum == 0 && !chksum_seen)) /* * If fs is mounted using an old kernel and then * kernel with journal_chksum is used then we * get a situation where the journal flag has * checksum flag set but checksums are not * present i.e chksum = 0, in the individual * commit blocks. * Hence to avoid checksum failures, in this * situation, this extra check is added. */ chksum_err = 1; if (chksum_err) { info->end_transaction = next_commit_ID; jbd_debug(1, "Checksum_err %x %x\n", crc32_sum, found_chksum); if (!JFS_HAS_INCOMPAT_FEATURE(journal, JFS_FEATURE_INCOMPAT_ASYNC_COMMIT)){ journal->j_failed_commit = next_commit_ID; brelse(bh); break; } } crc32_sum = ~0; } brelse(bh); next_commit_ID++; continue; case JFS_REVOKE_BLOCK: /* If we aren't in the REVOKE pass, then we can * just skip over this block. */ if (pass != PASS_REVOKE) { brelse(bh); continue; } err = scan_revoke_records(journal, bh, next_commit_ID, info); brelse(bh); if (err) goto failed; continue; default: jbd_debug(3, "Unrecognised magic %d, end of scan.\n", blocktype); brelse(bh); goto done; } } done: /* * We broke out of the log scan loop: either we came to the * known end of the log or we found an unexpected block in the * log. If the latter happened, then we know that the "current" * transaction marks the end of the valid log. */ if (pass == PASS_SCAN) { if (!info->end_transaction) info->end_transaction = next_commit_ID; } else { /* It's really bad news if different passes end up at * different places (but possible due to IO errors). */ if (info->end_transaction != next_commit_ID) { printk (KERN_ERR "JBD: recovery pass %d ended at " "transaction %u, expected %u\n", pass, next_commit_ID, info->end_transaction); if (!success) success = -EIO; } } return success; failed: return err; }
void *load_exe(char *filename, Exe_head *eh) { long retval; char *alligned_buf; char *alloc_buf = NULL; long (*rfunc)(); unsigned long code_offset; unsigned long init_size; unsigned long bss_size; unsigned long total_size; void *v; unsigned long fixup_offset; UWORD fixup[2]; UWORD *segpt; UWORD code_seg; int f; unsigned i; int ok = 0; if ((f = jopen(filename, JREADONLY)) == 0) { cant_find(filename); return(NULL); } if (!verify_exe_head(f, eh)) goto OUT; code_offset = eh->head_size; code_offset *= 16; /* make it a paragraph */ init_size = eh->blocks; init_size *= 512; init_size += eh->mod512; if (eh->mod512 != 0) init_size -= 512; init_size -= code_offset; bss_size = eh->min_data; bss_size *= 16; total_size = init_size + bss_size; if ((alloc_buf = begmem((unsigned)total_size+16)) == NULL) goto OUT; code_seg = ptr_seg(alloc_buf) + 1; alligned_buf = make_ptr(0, code_seg); zero_structure(alligned_buf, (unsigned)total_size); jseek(f, code_offset, JSEEK_START); if (jread(f, alligned_buf, init_size) < init_size) { truncated(filename); goto OUT; } v = alligned_buf; eh->entry_point = v; if (eh->reloc_count > 0) { fixup_offset = eh->reloc_list; jseek(f, fixup_offset, JSEEK_START); for (i=0; i<eh->reloc_count; i++) { if (jread(f, fixup, sizeof(fixup)) != sizeof(fixup)) { truncated(filename); goto OUT; } segpt = make_ptr(fixup[0], code_seg + fixup[1]); segpt[0] += code_seg; } } ok = 1; OUT: if (!ok) { gentle_freemem(alloc_buf); alloc_buf = NULL; } jclose(f); return(alloc_buf); }
int main(int ac, char **av) { const char *input_prefix = NULL; char *output_transid_file = NULL; char *mirror_transid_file = NULL; const char *mirror_directory = "."; char *record_prefix = NULL; char *record_transid_file = NULL; struct jsession jsdebug; struct jsession jsoutput; struct jsession jsmirror; char *ptr; int64_t mirror_transid; int64_t output_transid; int64_t record_transid; int64_t transid; int input_fd; struct stat st; struct jfile *jf; struct jdata *jd; int ch; while ((ch = getopt(ac, av, "2c:dfm:o:s:uvw:D:O:W:F")) != -1) { switch(ch) { case '2': jmodes |= JMODEF_INPUT_FULL; break; case 'c': trans_count = strtoll(optarg, &ptr, 0); switch(*ptr) { case 't': trans_count *= 1024; /* fall through */ case 'g': trans_count *= 1024; /* fall through */ case 'm': trans_count *= 1024; /* fall through */ case 'k': trans_count *= 1024; break; case 0: break; default: fprintf(stderr, "Bad suffix for value specified with -c, use 'k', 'm', 'g', 't', or nothing\n"); usage(av[0]); } break; case 'd': jmodes |= JMODEF_DEBUG; break; case 'f': jmodes |= JMODEF_LOOP_FOREVER; break; case 'v': ++verbose_opt; break; case 'm': jmodes |= JMODEF_MIRROR; if (strcmp(optarg, "none") != 0) mirror_transid_file = optarg; break; case 'O': jmodes |= JMODEF_OUTPUT_FULL; /* fall through */ case 'o': jmodes |= JMODEF_OUTPUT; if (strcmp(optarg, "none") != 0) output_transid_file = optarg; break; case 's': prefix_file_size = strtoll(optarg, &ptr, 0); switch(*ptr) { case 't': prefix_file_size *= 1024; /* fall through */ case 'g': prefix_file_size *= 1024; /* fall through */ case 'm': prefix_file_size *= 1024; /* fall through */ case 'k': prefix_file_size *= 1024; break; case 0: break; default: fprintf(stderr, "Bad suffix for value specified with -s, use 'k', 'm', 'g', 't', or nothing\n"); usage(av[0]); } break; case 'u': jdirection = JD_BACKWARDS; break; case 'W': jmodes |= JMODEF_RECORD_TMP; /* fall through */ case 'w': jmodes |= JMODEF_RECORD; record_prefix = optarg; asprintf(&record_transid_file, "%s.transid", record_prefix); break; case 'D': mirror_directory = optarg; break; case 'F': ++fsync_opt; break; default: fprintf(stderr, "unknown option: -%c\n", optopt); usage(av[0]); } } /* * Sanity checks */ if ((jmodes & JMODEF_COMMAND_MASK) == 0) usage(av[0]); if (optind > ac + 1) { fprintf(stderr, "Only one input file or prefix may be specified,\n" "or zero if stdin is to be the input.\n"); usage(av[0]); } if (strcmp(mirror_directory, ".") != 0) { struct stat sb; if (stat(mirror_directory, &sb) != 0) { perror ("Could not stat mirror directory"); usage(av[0]); } if (!S_ISDIR(sb.st_mode)) { fprintf (stderr, "Mirror directory '%s' is not a directory\n", mirror_directory); usage(av[0]); } } if (jdirection == JD_BACKWARDS && (jmodes & (JMODEF_RECORD|JMODEF_OUTPUT))) { fprintf(stderr, "Undo mode is only good in mirroring mode and " "cannot be mixed with other modes.\n"); exit(1); } /* * STEP1 - OPEN INPUT * * The input will either be a pipe, a regular file, or a journaling * file prefix. */ jf = NULL; if (optind == ac) { input_prefix = "<stdin>"; input_fd = 0; if (fstat(0, &st) < 0 || !S_ISREG(st.st_mode)) { jmodes |= JMODEF_INPUT_PIPE; if (jdirection == JD_BACKWARDS) { fprintf(stderr, "Cannot scan journals on pipes backwards\n"); usage(av[0]); } } jf = jopen_fd(input_fd); } else if (stat(av[optind], &st) == 0 && S_ISREG(st.st_mode)) { input_prefix = av[optind]; if ((input_fd = open(av[optind], O_RDONLY)) != 0) { jf = jopen_fd(input_fd); } else { jf = NULL; } } else { input_prefix = av[optind]; jf = jopen_prefix(input_prefix, 0); jmodes |= JMODEF_INPUT_PREFIX; } if (jf == NULL) { fprintf(stderr, "Unable to open input %s: %s\n", input_prefix, strerror(errno)); exit(1); } /* * STEP 1 - SYNCHRONIZING THE INPUT STREAM * * Figure out the starting point for our various output modes. Figure * out the earliest transaction id and try to seek to that point, * otherwise we might have to scan through terrabytes of data. * * Invalid transid's will be set to 0, but it should also be noted * that 0 is also a valid transid. */ get_transid_from_file(output_transid_file, &output_transid, JMODEF_OUTPUT_TRANSID_GOOD); get_transid_from_file(mirror_transid_file, &mirror_transid, JMODEF_MIRROR_TRANSID_GOOD); get_transid_from_file(record_transid_file, &record_transid, JMODEF_RECORD_TRANSID_GOOD); transid = LLONG_MAX; if ((jmodes & JMODEF_OUTPUT_TRANSID_GOOD) && output_transid < transid) transid = output_transid; if ((jmodes & JMODEF_MIRROR_TRANSID_GOOD) && mirror_transid < transid) transid = mirror_transid; if ((jmodes & JMODEF_RECORD_TRANSID_GOOD) && record_transid < transid) transid = record_transid; if ((jmodes & JMODEF_TRANSID_GOOD_MASK) == 0) transid = 0; if (verbose_opt) { if (jmodes & JMODEF_OUTPUT) { fprintf(stderr, "Starting transid for OUTPUT: %016jx\n", (uintmax_t)output_transid); } if (jmodes & JMODEF_MIRROR) { fprintf(stderr, "Starting transid for MIRROR: %016jx\n", (uintmax_t)mirror_transid); } if (jmodes & JMODEF_RECORD) { fprintf(stderr, "Starting transid for RECORD: %016jx\n", (uintmax_t)record_transid); } } if (strcmp(mirror_directory, ".") != 0) { if (chdir (mirror_directory) != 0) { perror ("Could not enter mirror directory"); exit (1); } } /* * Now it gets more difficult. If we are recording then the input * could be representative of continuing data and not have any * prior, older data that the output or mirror modes might need. Those * modes must work off the recording data even as we write to it. * In that case we fork and have the sub-processes work off the * record output. * * Then we take the input and start recording. */ if (jmodes & JMODEF_RECORD) { if (jrecord_init(record_prefix) < 0) { fprintf(stderr, "Unable to initialize file set for: %s\n", record_prefix); exit(1); } if (jmodes & JMODEF_MIRROR) { fork_subprocess(jf, jscan_do_mirror, record_prefix, mirror_transid_file, mirror_directory, mirror_transid); /* XXX ack stream for temporary record file removal */ } if (jmodes & JMODEF_OUTPUT) { fork_subprocess(jf, jscan_do_output, record_prefix, record_transid_file, NULL, output_transid); /* XXX ack stream for temporary record file removal */ } jscan_do_record(jf, record_transid_file, record_prefix, record_transid); exit(0); } /* * If the input is a prefix set we can just pass it to the appropriate * jscan_do_*() function. If we are doing both output and mirroring * we fork the mirror and do the output in the foreground since that * is going to stdout. */ if (jmodes & JMODEF_INPUT_PREFIX) { if ((jmodes & JMODEF_OUTPUT) && (jmodes & JMODEF_MIRROR)) { fork_subprocess(jf, jscan_do_mirror, input_prefix, mirror_transid_file, mirror_directory, mirror_transid); jscan_do_output(jf, output_transid_file, NULL, output_transid); } else if (jmodes & JMODEF_OUTPUT) { jscan_do_output(jf, output_transid_file, NULL, output_transid); } else if (jmodes & JMODEF_MIRROR) { jscan_do_mirror(jf, mirror_transid_file, mirror_directory, mirror_transid); } else if (jmodes & JMODEF_DEBUG) { jscan_do_debug(jf, NULL, NULL, 0); } exit(0); } /* * The input is not a prefix set and we are not recording, which means * we have to transfer the data on the input pipe to the output and * mirroring code on the fly. This also means that we must keep track * of meta-data records in-memory. However, if the input is a regular * file we *CAN* try to optimize where we start reading. * * NOTE: If the mirroring code encounters a transaction record that is * not marked begin, and it does not have the begin record, it will * attempt to locate the begin record if the input is not a pipe, then * seek back. */ if ((jmodes & JMODEF_TRANSID_GOOD_MASK) && !(jmodes & JMODEF_INPUT_PIPE)) jd = jseek(jf, transid, jdirection); else jd = jread(jf, NULL, jdirection); jmodes |= JMODEF_MEMORY_TRACKING; jsession_init(&jsdebug, jf, jdirection, NULL, 0); jsession_init(&jsoutput, jf, jdirection, output_transid_file, output_transid); jsession_init(&jsmirror, jf, jdirection, mirror_transid_file, mirror_transid); jsmirror.ss_mirror_directory = mirror_directory; while (jd != NULL) { if ((jmodes & JMODEF_DEBUG) && jsession_check(&jsdebug, jd)) dump_debug(&jsdebug, jd); if ((jmodes & JMODEF_OUTPUT) && jsession_check(&jsoutput, jd)) dump_output(&jsoutput, jd); if ((jmodes & JMODEF_MIRROR) && jsession_check(&jsmirror, jd)) dump_mirror(&jsmirror, jd); if (donecheck(jdirection, jd, transid)) { jfree(jf, jd); break; } jd = jread(jf, jd, jdirection); } jclose(jf); jsession_term(&jsdebug); jsession_term(&jsoutput); jsession_term(&jsmirror); return(0); }
static int do_one_pass(journal_t *journal, struct recovery_info *info, enum passtype pass) { unsigned int first_commit_ID, next_commit_ID; unsigned long next_log_block; int err, success = 0; journal_superblock_t * sb; journal_header_t * tmp; struct buffer_head * bh; unsigned int sequence; int blocktype; int tag_bytes = journal_tag_bytes(journal); __u32 crc32_sum = ~0; sb = journal->j_superblock; next_commit_ID = be32_to_cpu(sb->s_sequence); next_log_block = be32_to_cpu(sb->s_start); first_commit_ID = next_commit_ID; if (pass == PASS_SCAN) info->start_transaction = first_commit_ID; jbd_debug(1, "Starting recovery pass %d\n", pass); while (1) { int flags; char * tagp; journal_block_tag_t * tag; struct buffer_head * obh; struct buffer_head * nbh; cond_resched(); if (pass != PASS_SCAN) if (tid_geq(next_commit_ID, info->end_transaction)) break; jbd_debug(2, "Scanning for sequence ID %u at %lu/%lu\n", next_commit_ID, next_log_block, journal->j_last); jbd_debug(3, "JBD2: checking block %ld\n", next_log_block); err = jread(&bh, journal, next_log_block); if (err) goto failed; next_log_block++; wrap(journal, next_log_block); tmp = (journal_header_t *)bh->b_data; if (tmp->h_magic != cpu_to_be32(JBD2_MAGIC_NUMBER)) { brelse(bh); break; } blocktype = be32_to_cpu(tmp->h_blocktype); sequence = be32_to_cpu(tmp->h_sequence); jbd_debug(3, "Found magic %d, sequence %d\n", blocktype, sequence); if (sequence != next_commit_ID) { brelse(bh); break; } switch(blocktype) { case JBD2_DESCRIPTOR_BLOCK: if (pass != PASS_REPLAY) { if (pass == PASS_SCAN && JBD2_HAS_COMPAT_FEATURE(journal, JBD2_FEATURE_COMPAT_CHECKSUM) && !info->end_transaction) { if (calc_chksums(journal, bh, &next_log_block, &crc32_sum)) { put_bh(bh); break; } put_bh(bh); continue; } next_log_block += count_tags(journal, bh); wrap(journal, next_log_block); put_bh(bh); continue; } tagp = &bh->b_data[sizeof(journal_header_t)]; while ((tagp - bh->b_data + tag_bytes) <= journal->j_blocksize) { unsigned long io_block; tag = (journal_block_tag_t *) tagp; flags = be32_to_cpu(tag->t_flags); io_block = next_log_block++; wrap(journal, next_log_block); err = jread(&obh, journal, io_block); if (err) { success = err; printk(KERN_ERR "JBD2: IO error %d recovering " "block %ld in log\n", err, io_block); } else { unsigned long long blocknr; J_ASSERT(obh != NULL); blocknr = read_tag_block(tag_bytes, tag); if (jbd2_journal_test_revoke (journal, blocknr, next_commit_ID)) { brelse(obh); ++info->nr_revoke_hits; goto skip_write; } nbh = __getblk(journal->j_fs_dev, blocknr, journal->j_blocksize); if (nbh == NULL) { printk(KERN_ERR "JBD2: Out of memory " "during recovery.\n"); err = -ENOMEM; brelse(bh); brelse(obh); goto failed; } lock_buffer(nbh); memcpy(nbh->b_data, obh->b_data, journal->j_blocksize); if (flags & JBD2_FLAG_ESCAPE) { *((__be32 *)nbh->b_data) = cpu_to_be32(JBD2_MAGIC_NUMBER); } BUFFER_TRACE(nbh, "marking dirty"); set_buffer_uptodate(nbh); mark_buffer_dirty(nbh); BUFFER_TRACE(nbh, "marking uptodate"); ++info->nr_replays; unlock_buffer(nbh); brelse(obh); brelse(nbh); } skip_write: tagp += tag_bytes; if (!(flags & JBD2_FLAG_SAME_UUID)) tagp += 16; if (flags & JBD2_FLAG_LAST_TAG) break; } brelse(bh); continue; case JBD2_COMMIT_BLOCK: if (pass == PASS_SCAN && JBD2_HAS_COMPAT_FEATURE(journal, JBD2_FEATURE_COMPAT_CHECKSUM)) { int chksum_err, chksum_seen; struct commit_header *cbh = (struct commit_header *)bh->b_data; unsigned found_chksum = be32_to_cpu(cbh->h_chksum[0]); chksum_err = chksum_seen = 0; if (info->end_transaction) { journal->j_failed_commit = info->end_transaction; brelse(bh); break; } if (crc32_sum == found_chksum && cbh->h_chksum_type == JBD2_CRC32_CHKSUM && cbh->h_chksum_size == JBD2_CRC32_CHKSUM_SIZE) chksum_seen = 1; else if (!(cbh->h_chksum_type == 0 && cbh->h_chksum_size == 0 && found_chksum == 0 && !chksum_seen)) chksum_err = 1; if (chksum_err) { info->end_transaction = next_commit_ID; if (!JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT)){ journal->j_failed_commit = next_commit_ID; brelse(bh); break; } } crc32_sum = ~0; } brelse(bh); next_commit_ID++; continue; case JBD2_REVOKE_BLOCK: if (pass != PASS_REVOKE) { brelse(bh); continue; } err = scan_revoke_records(journal, bh, next_commit_ID, info); brelse(bh); if (err) goto failed; continue; default: jbd_debug(3, "Unrecognised magic %d, end of scan.\n", blocktype); brelse(bh); goto done; } } done: if (pass == PASS_SCAN) { if (!info->end_transaction) info->end_transaction = next_commit_ID; } else { if (info->end_transaction != next_commit_ID) { printk(KERN_ERR "JBD2: recovery pass %d ended at " "transaction %u, expected %u\n", pass, next_commit_ID, info->end_transaction); if (!success) success = -EIO; } } return success; failed: return err; }