summaryrefslogtreecommitdiff
path: root/libbcachefs/journal.c
diff options
context:
space:
mode:
Diffstat (limited to 'libbcachefs/journal.c')
-rw-r--r--libbcachefs/journal.c145
1 files changed, 92 insertions, 53 deletions
diff --git a/libbcachefs/journal.c b/libbcachefs/journal.c
index f6203f1e..ca96330c 100644
--- a/libbcachefs/journal.c
+++ b/libbcachefs/journal.c
@@ -527,62 +527,34 @@ fsck_err:
#define JOURNAL_ENTRY_NONE 6
#define JOURNAL_ENTRY_BAD 7
-static int journal_entry_validate(struct bch_fs *c,
- struct jset *j, u64 sector,
- unsigned bucket_sectors_left,
- unsigned sectors_read)
+#define journal_entry_err(c, msg, ...) \
+({ \
+ if (write == READ) { \
+ mustfix_fsck_err(c, msg, ##__VA_ARGS__); \
+ } else { \
+ bch_err(c, "detected corrupt metadata before write:\n" \
+ msg, ##__VA_ARGS__); \
+ ret = BCH_FSCK_ERRORS_NOT_FIXED; \
+ goto fsck_err; \
+ } \
+ true; \
+})
+
+#define journal_entry_err_on(cond, c, msg, ...) \
+ ((cond) ? journal_entry_err(c, msg, ##__VA_ARGS__) : false)
+
+static int __journal_entry_validate(struct bch_fs *c, struct jset *j,
+ int write)
{
struct jset_entry *entry;
- size_t bytes = vstruct_bytes(j);
- struct bch_csum csum;
int ret = 0;
- if (le64_to_cpu(j->magic) != jset_magic(c))
- return JOURNAL_ENTRY_NONE;
-
- if (le32_to_cpu(j->version) != BCACHE_JSET_VERSION) {
- bch_err(c, "unknown journal entry version %u",
- le32_to_cpu(j->version));
- return BCH_FSCK_UNKNOWN_VERSION;
- }
-
- if (mustfix_fsck_err_on(bytes > bucket_sectors_left << 9, c,
- "journal entry too big (%zu bytes), sector %lluu",
- bytes, sector)) {
- /* XXX: note we might have missing journal entries */
- return JOURNAL_ENTRY_BAD;
- }
-
- if (bytes > sectors_read << 9)
- return JOURNAL_ENTRY_REREAD;
-
- if (fsck_err_on(!bch2_checksum_type_valid(c, JSET_CSUM_TYPE(j)), c,
- "journal entry with unknown csum type %llu sector %lluu",
- JSET_CSUM_TYPE(j), sector))
- return JOURNAL_ENTRY_BAD;
-
- csum = csum_vstruct(c, JSET_CSUM_TYPE(j), journal_nonce(j), j);
- if (mustfix_fsck_err_on(bch2_crc_cmp(csum, j->csum), c,
- "journal checksum bad, sector %llu", sector)) {
- /* XXX: retry IO, when we start retrying checksum errors */
- /* XXX: note we might have missing journal entries */
- return JOURNAL_ENTRY_BAD;
- }
-
- bch2_encrypt(c, JSET_CSUM_TYPE(j), journal_nonce(j),
- j->encrypted_start,
- vstruct_end(j) - (void *) j->encrypted_start);
-
- if (mustfix_fsck_err_on(le64_to_cpu(j->last_seq) > le64_to_cpu(j->seq), c,
- "invalid journal entry: last_seq > seq"))
- j->last_seq = j->seq;
-
vstruct_for_each(j, entry) {
struct bkey_i *k;
- if (mustfix_fsck_err_on(vstruct_next(entry) >
- vstruct_last(j), c,
- "journal entry extents past end of jset")) {
+ if (journal_entry_err_on(vstruct_next(entry) >
+ vstruct_last(j), c,
+ "journal entry extends past end of jset")) {
j->u64s = cpu_to_le64((u64 *) entry - j->_data);
break;
}
@@ -602,7 +574,7 @@ static int journal_entry_validate(struct bch_fs *c,
case JOURNAL_ENTRY_BTREE_ROOT:
k = entry->start;
- if (mustfix_fsck_err_on(!entry->u64s ||
+ if (journal_entry_err_on(!entry->u64s ||
le16_to_cpu(entry->u64s) != k->k.u64s, c,
"invalid btree root journal entry: wrong number of keys")) {
journal_entry_null_range(entry,
@@ -620,7 +592,7 @@ static int journal_entry_validate(struct bch_fs *c,
break;
case JOURNAL_ENTRY_JOURNAL_SEQ_BLACKLISTED:
- if (mustfix_fsck_err_on(le16_to_cpu(entry->u64s) != 1, c,
+ if (journal_entry_err_on(le16_to_cpu(entry->u64s) != 1, c,
"invalid journal seq blacklist entry: bad size")) {
journal_entry_null_range(entry,
vstruct_next(entry));
@@ -628,7 +600,7 @@ static int journal_entry_validate(struct bch_fs *c,
break;
default:
- mustfix_fsck_err(c, "invalid journal entry type %llu",
+ journal_entry_err(c, "invalid journal entry type %llu",
JOURNAL_ENTRY_TYPE(entry));
journal_entry_null_range(entry, vstruct_next(entry));
break;
@@ -639,6 +611,61 @@ fsck_err:
return ret;
}
+static int journal_entry_validate(struct bch_fs *c,
+ struct jset *j, u64 sector,
+ unsigned bucket_sectors_left,
+ unsigned sectors_read,
+ int write)
+{
+ size_t bytes = vstruct_bytes(j);
+ struct bch_csum csum;
+ int ret = 0;
+
+ if (le64_to_cpu(j->magic) != jset_magic(c))
+ return JOURNAL_ENTRY_NONE;
+
+ if (le32_to_cpu(j->version) != BCACHE_JSET_VERSION) {
+ bch_err(c, "unknown journal entry version %u",
+ le32_to_cpu(j->version));
+ return BCH_FSCK_UNKNOWN_VERSION;
+ }
+
+ if (journal_entry_err_on(bytes > bucket_sectors_left << 9, c,
+ "journal entry too big (%zu bytes), sector %lluu",
+ bytes, sector)) {
+ /* XXX: note we might have missing journal entries */
+ return JOURNAL_ENTRY_BAD;
+ }
+
+ if (bytes > sectors_read << 9)
+ return JOURNAL_ENTRY_REREAD;
+
+ if (fsck_err_on(!bch2_checksum_type_valid(c, JSET_CSUM_TYPE(j)), c,
+ "journal entry with unknown csum type %llu sector %lluu",
+ JSET_CSUM_TYPE(j), sector))
+ return JOURNAL_ENTRY_BAD;
+
+ csum = csum_vstruct(c, JSET_CSUM_TYPE(j), journal_nonce(j), j);
+ if (journal_entry_err_on(bch2_crc_cmp(csum, j->csum), c,
+ "journal checksum bad, sector %llu", sector)) {
+ /* XXX: retry IO, when we start retrying checksum errors */
+ /* XXX: note we might have missing journal entries */
+ return JOURNAL_ENTRY_BAD;
+ }
+
+ bch2_encrypt(c, JSET_CSUM_TYPE(j), journal_nonce(j),
+ j->encrypted_start,
+ vstruct_end(j) - (void *) j->encrypted_start);
+
+ if (journal_entry_err_on(le64_to_cpu(j->last_seq) > le64_to_cpu(j->seq), c,
+ "invalid journal entry: last_seq > seq"))
+ j->last_seq = j->seq;
+
+ return __journal_entry_validate(c, j, write);
+fsck_err:
+ return ret;
+}
+
struct journal_read_buf {
void *data;
size_t size;
@@ -705,7 +732,8 @@ reread: sectors_read = min_t(unsigned,
}
ret = journal_entry_validate(c, j, offset,
- end - offset, sectors_read);
+ end - offset, sectors_read,
+ READ);
switch (ret) {
case BCH_FSCK_OK:
break;
@@ -2274,6 +2302,10 @@ static void journal_write(struct closure *cl)
SET_JSET_BIG_ENDIAN(jset, CPU_BIG_ENDIAN);
SET_JSET_CSUM_TYPE(jset, bch2_meta_checksum_type(c));
+ if (bch2_csum_type_is_encryption(JSET_CSUM_TYPE(jset)) &&
+ __journal_entry_validate(c, jset, WRITE))
+ goto err;
+
bch2_encrypt(c, JSET_CSUM_TYPE(jset), journal_nonce(jset),
jset->encrypted_start,
vstruct_end(jset) - (void *) jset->encrypted_start);
@@ -2281,6 +2313,10 @@ static void journal_write(struct closure *cl)
jset->csum = csum_vstruct(c, JSET_CSUM_TYPE(jset),
journal_nonce(jset), jset);
+ if (!bch2_csum_type_is_encryption(JSET_CSUM_TYPE(jset)) &&
+ __journal_entry_validate(c, jset, WRITE))
+ goto err;
+
sectors = vstruct_sectors(jset, c->block_bits);
BUG_ON(sectors > j->prev_buf_sectors);
@@ -2349,6 +2385,9 @@ no_io:
ptr->offset += sectors;
closure_return_with_destructor(cl, journal_write_done);
+err:
+ bch2_fatal_error(c);
+ closure_return_with_destructor(cl, journal_write_done);
}
static void journal_write_work(struct work_struct *work)