diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2025-05-02 09:12:29 -0700 | 
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2025-05-02 09:12:29 -0700 | 
| commit | 2bfcee565c3a36f2781152a767d34c9dc5432f95 (patch) | |
| tree | 9749ce1c8d36d4051e1481484d5b85229b0adc5c | |
| parent | 85951e19c425d5c45c8d4119ee2bade518252e31 (diff) | |
| parent | 6846100b00d97d3d6f05766ae86a0d821d849e78 (diff) | |
Merge tag 'bcachefs-2025-05-01' of git://evilpiepirate.org/bcachefs
Pull bcachefs fixes from Kent Overstreet:
 "Lots of assorted small fixes...
   - Some repair path fixes, a fix for -ENOMEM when reconstructing lots
     of alloc info on large filesystems, upgrade for ancient 0.14
     filesystems, etc.
   - Various assert tweaks; assert -> ERO, ERO -> log the error in the
     superblock and continue
   - casefolding now uses d_ops like on other casefolding filesystems
   - fix device label create on device add, fix bucket array resize on
     filesystem resize
   - fix xattrs with FORTIFY_SOURCE builds with gcc-15/clang"
* tag 'bcachefs-2025-05-01' of git://evilpiepirate.org/bcachefs: (22 commits)
  bcachefs: Remove incorrect __counted_by annotation
  bcachefs: add missing sched_annotate_sleep()
  bcachefs: Fix __bch2_dev_group_set()
  bcachefs: Kill ERO for i_blocks check in truncate
  bcachefs: check for inode.bi_sectors underflow
  bcachefs: Kill ERO in __bch2_i_sectors_acct()
  bcachefs: readdir fixes
  bcachefs: improve missing journal write device error message
  bcachefs: Topology error after insert is now an ERO
  bcachefs: Use bch2_kvmalloc() for journal keys array
  bcachefs: More informative error message when shutting down due to error
  bcachefs: btree_root_unreadable_and_scan_found_nothing autofix for non data btrees
  bcachefs: btree_node_data_missing is now autofix
  bcachefs: Don't generate alloc updates to invalid buckets
  bcachefs: Improve bch2_dev_bucket_missing()
  bcachefs: fix bch2_dev_buckets_resize()
  bcachefs: Add upgrade table entry from 0.14
  bcachefs: Run BCH_RECOVERY_PASS_reconstruct_snapshots on missing subvol -> snapshot
  bcachefs: Add missing utf8_unload()
  bcachefs: Emit unicode version message on startup
  ...
| -rw-r--r-- | fs/bcachefs/btree_gc.c | 27 | ||||
| -rw-r--r-- | fs/bcachefs/btree_journal_iter.c | 2 | ||||
| -rw-r--r-- | fs/bcachefs/btree_update_interior.c | 49 | ||||
| -rw-r--r-- | fs/bcachefs/buckets.c | 15 | ||||
| -rw-r--r-- | fs/bcachefs/dirent.c | 4 | ||||
| -rw-r--r-- | fs/bcachefs/disk_groups.c | 25 | ||||
| -rw-r--r-- | fs/bcachefs/ec.c | 4 | ||||
| -rw-r--r-- | fs/bcachefs/error.c | 4 | ||||
| -rw-r--r-- | fs/bcachefs/fs-io.c | 44 | ||||
| -rw-r--r-- | fs/bcachefs/fs.c | 15 | ||||
| -rw-r--r-- | fs/bcachefs/io_write.c | 21 | ||||
| -rw-r--r-- | fs/bcachefs/journal_io.c | 2 | ||||
| -rw-r--r-- | fs/bcachefs/namei.c | 3 | ||||
| -rw-r--r-- | fs/bcachefs/sb-downgrade.c | 4 | ||||
| -rw-r--r-- | fs/bcachefs/sb-errors_format.h | 13 | ||||
| -rw-r--r-- | fs/bcachefs/sb-members.c | 6 | ||||
| -rw-r--r-- | fs/bcachefs/sb-members.h | 13 | ||||
| -rw-r--r-- | fs/bcachefs/subvolume.c | 5 | ||||
| -rw-r--r-- | fs/bcachefs/super.c | 46 | ||||
| -rw-r--r-- | fs/bcachefs/xattr_format.h | 8 | 
20 files changed, 219 insertions, 91 deletions
| diff --git a/fs/bcachefs/btree_gc.c b/fs/bcachefs/btree_gc.c index 7b98ba2dec64..37b69d89341f 100644 --- a/fs/bcachefs/btree_gc.c +++ b/fs/bcachefs/btree_gc.c @@ -47,6 +47,27 @@  #define DROP_PREV_NODE		11  #define DID_FILL_FROM_SCAN	12 +/* + * Returns true if it's a btree we can easily reconstruct, or otherwise won't + * cause data loss if it's missing: + */ +static bool btree_id_important(enum btree_id btree) +{ +	if (btree_id_is_alloc(btree)) +		return false; + +	switch (btree) { +	case BTREE_ID_quotas: +	case BTREE_ID_snapshot_trees: +	case BTREE_ID_logged_ops: +	case BTREE_ID_rebalance_work: +	case BTREE_ID_subvolume_children: +		return false; +	default: +		return true; +	} +} +  static const char * const bch2_gc_phase_strs[] = {  #define x(n)	#n,  	GC_PHASES() @@ -534,8 +555,10 @@ reconstruct_root:  			r->error = 0;  			if (!bch2_btree_has_scanned_nodes(c, i)) { -				mustfix_fsck_err(trans, btree_root_unreadable_and_scan_found_nothing, -						 "no nodes found for btree %s, continue?", buf.buf); +				__fsck_err(trans, +					   FSCK_CAN_FIX|(!btree_id_important(i) ? FSCK_AUTOFIX : 0), +					   btree_root_unreadable_and_scan_found_nothing, +					   "no nodes found for btree %s, continue?", buf.buf);  				bch2_btree_root_alloc_fake_trans(trans, i, 0);  			} else {  				bch2_btree_root_alloc_fake_trans(trans, i, 1); diff --git a/fs/bcachefs/btree_journal_iter.c b/fs/bcachefs/btree_journal_iter.c index 7d6c971db23c..ade3b5addd75 100644 --- a/fs/bcachefs/btree_journal_iter.c +++ b/fs/bcachefs/btree_journal_iter.c @@ -288,7 +288,7 @@ int bch2_journal_key_insert_take(struct bch_fs *c, enum btree_id id,  			.size			= max_t(size_t, keys->size, 8) * 2,  		}; -		new_keys.data = kvmalloc_array(new_keys.size, sizeof(new_keys.data[0]), GFP_KERNEL); +		new_keys.data = bch2_kvmalloc(new_keys.size * sizeof(new_keys.data[0]), GFP_KERNEL);  		if (!new_keys.data) {  			bch_err(c, "%s: error allocating new key array (size %zu)",  				__func__, new_keys.size); diff --git a/fs/bcachefs/btree_update_interior.c b/fs/bcachefs/btree_update_interior.c index 44b5fe430370..00307356d7c8 100644 --- a/fs/bcachefs/btree_update_interior.c +++ b/fs/bcachefs/btree_update_interior.c @@ -1389,7 +1389,7 @@ static void bch2_insert_fixup_btree_ptr(struct btree_update *as,  	printbuf_exit(&buf);  } -static void +static int  bch2_btree_insert_keys_interior(struct btree_update *as,  				struct btree_trans *trans,  				struct btree_path *path, @@ -1411,7 +1411,8 @@ bch2_btree_insert_keys_interior(struct btree_update *as,  	     insert = bkey_next(insert))  		bch2_insert_fixup_btree_ptr(as, trans, path, b, &node_iter, insert); -	if (bch2_btree_node_check_topology(trans, b)) { +	int ret = bch2_btree_node_check_topology(trans, b); +	if (ret) {  		struct printbuf buf = PRINTBUF;  		for (struct bkey_i *k = keys->keys; @@ -1421,11 +1422,15 @@ bch2_btree_insert_keys_interior(struct btree_update *as,  			prt_newline(&buf);  		} -		panic("%s(): check_topology error: inserted keys\n%s", __func__, buf.buf); +		bch2_fs_fatal_error(as->c, "%ps -> %s(): check_topology error %s: inserted keys\n%s", +				    (void *) _RET_IP_, __func__, bch2_err_str(ret), buf.buf); +		dump_stack(); +		return ret;  	}  	memmove_u64s_down(keys->keys, insert, keys->top_p - insert->_data);  	keys->top_p -= insert->_data - keys->keys_p; +	return 0;  }  static bool key_deleted_in_insert(struct keylist *insert_keys, struct bpos pos) @@ -1559,11 +1564,11 @@ static void __btree_split_node(struct btree_update *as,   * nodes that were coalesced, and thus in the middle of a child node post   * coalescing:   */ -static void btree_split_insert_keys(struct btree_update *as, -				    struct btree_trans *trans, -				    btree_path_idx_t path_idx, -				    struct btree *b, -				    struct keylist *keys) +static int btree_split_insert_keys(struct btree_update *as, +				   struct btree_trans *trans, +				   btree_path_idx_t path_idx, +				   struct btree *b, +				   struct keylist *keys)  {  	struct btree_path *path = trans->paths + path_idx; @@ -1573,8 +1578,12 @@ static void btree_split_insert_keys(struct btree_update *as,  		bch2_btree_node_iter_init(&node_iter, b, &bch2_keylist_front(keys)->k.p); -		bch2_btree_insert_keys_interior(as, trans, path, b, node_iter, keys); +		int ret = bch2_btree_insert_keys_interior(as, trans, path, b, node_iter, keys); +		if (ret) +			return ret;  	} + +	return 0;  }  static int btree_split(struct btree_update *as, struct btree_trans *trans, @@ -1607,8 +1616,10 @@ static int btree_split(struct btree_update *as, struct btree_trans *trans,  		__btree_split_node(as, trans, b, n, keys);  		if (keys) { -			btree_split_insert_keys(as, trans, path, n1, keys); -			btree_split_insert_keys(as, trans, path, n2, keys); +			ret =   btree_split_insert_keys(as, trans, path, n1, keys) ?: +				btree_split_insert_keys(as, trans, path, n2, keys); +			if (ret) +				goto err;  			BUG_ON(!bch2_keylist_empty(keys));  		} @@ -1654,7 +1665,9 @@ static int btree_split(struct btree_update *as, struct btree_trans *trans,  			n3->sib_u64s[0] = U16_MAX;  			n3->sib_u64s[1] = U16_MAX; -			btree_split_insert_keys(as, trans, path, n3, &as->parent_keys); +			ret = btree_split_insert_keys(as, trans, path, n3, &as->parent_keys); +			if (ret) +				goto err;  		}  	} else {  		trace_and_count(c, btree_node_compact, trans, b); @@ -1662,7 +1675,9 @@ static int btree_split(struct btree_update *as, struct btree_trans *trans,  		n1 = bch2_btree_node_alloc_replacement(as, trans, b);  		if (keys) { -			btree_split_insert_keys(as, trans, path, n1, keys); +			ret = btree_split_insert_keys(as, trans, path, n1, keys); +			if (ret) +				goto err;  			BUG_ON(!bch2_keylist_empty(keys));  		} @@ -1809,15 +1824,15 @@ static int bch2_btree_insert_node(struct btree_update *as, struct btree_trans *t  		goto split;  	} -	ret = bch2_btree_node_check_topology(trans, b); + +	ret =   bch2_btree_node_check_topology(trans, b) ?: +		bch2_btree_insert_keys_interior(as, trans, path, b, +					path->l[b->c.level].iter, keys);  	if (ret) {  		bch2_btree_node_unlock_write(trans, path, b);  		return ret;  	} -	bch2_btree_insert_keys_interior(as, trans, path, b, -					path->l[b->c.level].iter, keys); -  	trans_for_each_path_with_node(trans, b, linked, i)  		bch2_btree_node_iter_peek(&linked->l[b->c.level].iter, b); diff --git a/fs/bcachefs/buckets.c b/fs/bcachefs/buckets.c index 4ef261e8db4f..31fbc2716d8b 100644 --- a/fs/bcachefs/buckets.c +++ b/fs/bcachefs/buckets.c @@ -604,6 +604,13 @@ static int bch2_trigger_pointer(struct btree_trans *trans,  	}  	struct bpos bucket = PTR_BUCKET_POS(ca, &p.ptr); +	if (!bucket_valid(ca, bucket.offset)) { +		if (insert) { +			bch2_dev_bucket_missing(ca, bucket.offset); +			ret = -BCH_ERR_trigger_pointer; +		} +		goto err; +	}  	if (flags & BTREE_TRIGGER_transactional) {  		struct bkey_i_alloc_v4 *a = bch2_trans_start_alloc_update(trans, bucket, 0); @@ -1307,13 +1314,11 @@ int bch2_dev_buckets_resize(struct bch_fs *c, struct bch_dev *ca, u64 nbuckets)  	old_bucket_gens = rcu_dereference_protected(ca->bucket_gens, 1);  	if (resize) { -		bucket_gens->nbuckets = min(bucket_gens->nbuckets, -					    old_bucket_gens->nbuckets); -		bucket_gens->nbuckets_minus_first = -			bucket_gens->nbuckets - bucket_gens->first_bucket; +		u64 copy = min(bucket_gens->nbuckets, +			       old_bucket_gens->nbuckets);  		memcpy(bucket_gens->b,  		       old_bucket_gens->b, -		       bucket_gens->nbuckets); +		       sizeof(bucket_gens->b[0]) * copy);  	}  	rcu_assign_pointer(ca->bucket_gens, bucket_gens); diff --git a/fs/bcachefs/dirent.c b/fs/bcachefs/dirent.c index 92ee59d9e00e..8a680e52c1ed 100644 --- a/fs/bcachefs/dirent.c +++ b/fs/bcachefs/dirent.c @@ -685,7 +685,7 @@ static int bch2_dir_emit(struct dir_context *ctx, struct bkey_s_c_dirent d, subv  		      vfs_d_type(d.v->d_type));  	if (ret)  		ctx->pos = d.k->p.offset + 1; -	return ret; +	return !ret;  }  int bch2_readdir(struct bch_fs *c, subvol_inum inum, struct dir_context *ctx) @@ -710,7 +710,7 @@ int bch2_readdir(struct bch_fs *c, subvol_inum inum, struct dir_context *ctx)  			if (ret2 > 0)  				continue; -			ret2 ?: drop_locks_do(trans, bch2_dir_emit(ctx, dirent, target)); +			ret2 ?: (bch2_trans_unlock(trans), bch2_dir_emit(ctx, dirent, target));  		})));  	bch2_bkey_buf_exit(&sk, c); diff --git a/fs/bcachefs/disk_groups.c b/fs/bcachefs/disk_groups.c index 1186280b29e9..2ca3cbf12b71 100644 --- a/fs/bcachefs/disk_groups.c +++ b/fs/bcachefs/disk_groups.c @@ -470,23 +470,22 @@ inval:  int __bch2_dev_group_set(struct bch_fs *c, struct bch_dev *ca, const char *name)  { -	struct bch_member *mi; -	int ret, v = -1; +	lockdep_assert_held(&c->sb_lock); -	if (!strlen(name) || !strcmp(name, "none")) -		return 0; -	v = bch2_disk_path_find_or_create(&c->disk_sb, name); -	if (v < 0) -		return v; +	if (!strlen(name) || !strcmp(name, "none")) { +		struct bch_member *mi = bch2_members_v2_get_mut(c->disk_sb.sb, ca->dev_idx); +		SET_BCH_MEMBER_GROUP(mi, 0); +	} else { +		int v = bch2_disk_path_find_or_create(&c->disk_sb, name); +		if (v < 0) +			return v; -	ret = bch2_sb_disk_groups_to_cpu(c); -	if (ret) -		return ret; +		struct bch_member *mi = bch2_members_v2_get_mut(c->disk_sb.sb, ca->dev_idx); +		SET_BCH_MEMBER_GROUP(mi, v + 1); +	} -	mi = bch2_members_v2_get_mut(c->disk_sb.sb, ca->dev_idx); -	SET_BCH_MEMBER_GROUP(mi, v + 1); -	return 0; +	return bch2_sb_disk_groups_to_cpu(c);  }  int bch2_dev_group_set(struct bch_fs *c, struct bch_dev *ca, const char *name) diff --git a/fs/bcachefs/ec.c b/fs/bcachefs/ec.c index a396865e8b17..fff58b78327c 100644 --- a/fs/bcachefs/ec.c +++ b/fs/bcachefs/ec.c @@ -2204,10 +2204,10 @@ void bch2_fs_ec_stop(struct bch_fs *c)  static bool bch2_fs_ec_flush_done(struct bch_fs *c)  { -	bool ret; +	sched_annotate_sleep();  	mutex_lock(&c->ec_stripe_new_lock); -	ret = list_empty(&c->ec_stripe_new_list); +	bool ret = list_empty(&c->ec_stripe_new_list);  	mutex_unlock(&c->ec_stripe_new_lock);  	return ret; diff --git a/fs/bcachefs/error.c b/fs/bcachefs/error.c index 925b0b54ea2f..6b8695b1349c 100644 --- a/fs/bcachefs/error.c +++ b/fs/bcachefs/error.c @@ -478,7 +478,9 @@ int __bch2_fsck_err(struct bch_fs *c,  	} else if (!test_bit(BCH_FS_fsck_running, &c->flags)) {  		if (c->opts.errors != BCH_ON_ERROR_continue ||  		    !(flags & (FSCK_CAN_FIX|FSCK_CAN_IGNORE))) { -			prt_str(out, ", shutting down"); +			prt_str_indented(out, ", shutting down\n" +					 "error not marked as autofix and not in fsck\n" +					 "run fsck, and forward to devs so error can be marked for self-healing");  			inconsistent = true;  			print = true;  			ret = -BCH_ERR_fsck_errors_not_fixed; diff --git a/fs/bcachefs/fs-io.c b/fs/bcachefs/fs-io.c index 65c2c33d253d..9657144666b8 100644 --- a/fs/bcachefs/fs-io.c +++ b/fs/bcachefs/fs-io.c @@ -144,10 +144,25 @@ int __must_check bch2_write_inode_size(struct bch_fs *c,  void __bch2_i_sectors_acct(struct bch_fs *c, struct bch_inode_info *inode,  			   struct quota_res *quota_res, s64 sectors)  { -	bch2_fs_inconsistent_on((s64) inode->v.i_blocks + sectors < 0, c, -				"inode %lu i_blocks underflow: %llu + %lli < 0 (ondisk %lli)", -				inode->v.i_ino, (u64) inode->v.i_blocks, sectors, -				inode->ei_inode.bi_sectors); +	if (unlikely((s64) inode->v.i_blocks + sectors < 0)) { +		struct printbuf buf = PRINTBUF; +		bch2_log_msg_start(c, &buf); +		prt_printf(&buf, "inode %lu i_blocks underflow: %llu + %lli < 0 (ondisk %lli)", +			   inode->v.i_ino, (u64) inode->v.i_blocks, sectors, +			   inode->ei_inode.bi_sectors); + +		bool repeat = false, print = false, suppress = false; +		bch2_count_fsck_err(c, vfs_inode_i_blocks_underflow, buf.buf, &repeat, &print, &suppress); +		if (print) +			bch2_print_str(c, buf.buf); +		printbuf_exit(&buf); + +		if (sectors < 0) +			sectors = -inode->v.i_blocks; +		else +			sectors = 0; +	} +  	inode->v.i_blocks += sectors;  #ifdef CONFIG_BCACHEFS_QUOTA @@ -502,11 +517,22 @@ int bchfs_truncate(struct mnt_idmap *idmap,  		goto err;  	} -	bch2_fs_inconsistent_on(!inode->v.i_size && inode->v.i_blocks && -				!bch2_journal_error(&c->journal), c, -				"inode %lu truncated to 0 but i_blocks %llu (ondisk %lli)", -				inode->v.i_ino, (u64) inode->v.i_blocks, -				inode->ei_inode.bi_sectors); +	if (unlikely(!inode->v.i_size && inode->v.i_blocks && +		     !bch2_journal_error(&c->journal))) { +		struct printbuf buf = PRINTBUF; +		bch2_log_msg_start(c, &buf); +		prt_printf(&buf, +			   "inode %lu truncated to 0 but i_blocks %llu (ondisk %lli)", +			   inode->v.i_ino, (u64) inode->v.i_blocks, +			   inode->ei_inode.bi_sectors); + +		bool repeat = false, print = false, suppress = false; +		bch2_count_fsck_err(c, vfs_inode_i_blocks_not_zero_at_truncate, buf.buf, +				    &repeat, &print, &suppress); +		if (print) +			bch2_print_str(c, buf.buf); +		printbuf_exit(&buf); +	}  	ret = bch2_setattr_nonsize(idmap, inode, iattr);  err: diff --git a/fs/bcachefs/fs.c b/fs/bcachefs/fs.c index 0f1d61aab90b..113db85b6ef9 100644 --- a/fs/bcachefs/fs.c +++ b/fs/bcachefs/fs.c @@ -66,6 +66,8 @@ static inline void bch2_inode_flags_to_vfs(struct bch_fs *c, struct bch_inode_in  	if (bch2_inode_casefold(c, &inode->ei_inode))  		inode->v.i_flags |= S_CASEFOLD; +	else +		inode->v.i_flags &= ~S_CASEFOLD;  }  void bch2_inode_update_after_write(struct btree_trans *trans, @@ -848,10 +850,8 @@ int __bch2_unlink(struct inode *vdir, struct dentry *dentry,  		set_nlink(&inode->v, 0);  	} -	if (IS_CASEFOLDED(vdir)) { +	if (IS_CASEFOLDED(vdir))  		d_invalidate(dentry); -		d_prune_aliases(&inode->v); -	}  err:  	bch2_trans_put(trans);  	bch2_unlock_inodes(INODE_UPDATE_LOCK, dir, inode); @@ -1464,8 +1464,8 @@ static int bch2_next_fiemap_extent(struct btree_trans *trans,  		unsigned sectors = cur->kbuf.k->k.size;  		s64 offset_into_extent = 0;  		enum btree_id data_btree = BTREE_ID_extents; -		int ret = bch2_read_indirect_extent(trans, &data_btree, &offset_into_extent, -						    &cur->kbuf); +		ret = bch2_read_indirect_extent(trans, &data_btree, &offset_into_extent, +						&cur->kbuf);  		if (ret)  			goto err; @@ -2571,6 +2571,11 @@ got_sb:  	if (ret)  		goto err_put_super; +#ifdef CONFIG_UNICODE +	sb->s_encoding = c->cf_encoding; +#endif +	generic_set_sb_d_ops(sb); +  	vinode = bch2_vfs_inode_get(c, BCACHEFS_ROOT_SUBVOL_INUM);  	ret = PTR_ERR_OR_ZERO(vinode);  	bch_err_msg(c, ret, "mounting: error getting root inode"); diff --git a/fs/bcachefs/io_write.c b/fs/bcachefs/io_write.c index a418fa62f09d..c1237da079ed 100644 --- a/fs/bcachefs/io_write.c +++ b/fs/bcachefs/io_write.c @@ -255,6 +255,27 @@ static inline int bch2_extent_update_i_size_sectors(struct btree_trans *trans,  	}  	if (i_sectors_delta) { +		s64 bi_sectors = le64_to_cpu(inode->v.bi_sectors); +		if (unlikely(bi_sectors + i_sectors_delta < 0)) { +			struct bch_fs *c = trans->c; +			struct printbuf buf = PRINTBUF; +			bch2_log_msg_start(c, &buf); +			prt_printf(&buf, "inode %llu i_sectors underflow: %lli + %lli < 0", +				   extent_iter->pos.inode, bi_sectors, i_sectors_delta); + +			bool repeat = false, print = false, suppress = false; +			bch2_count_fsck_err(c, inode_i_sectors_underflow, buf.buf, +					    &repeat, &print, &suppress); +			if (print) +				bch2_print_str(c, buf.buf); +			printbuf_exit(&buf); + +			if (i_sectors_delta < 0) +				i_sectors_delta = -bi_sectors; +			else +				i_sectors_delta = 0; +		} +  		le64_add_cpu(&inode->v.bi_sectors, i_sectors_delta);  		inode_update_flags = 0;  	} diff --git a/fs/bcachefs/journal_io.c b/fs/bcachefs/journal_io.c index 2a54ac79189b..63cdf885c9e2 100644 --- a/fs/bcachefs/journal_io.c +++ b/fs/bcachefs/journal_io.c @@ -1782,7 +1782,7 @@ static CLOSURE_CALLBACK(journal_write_submit)  		struct bch_dev *ca = bch2_dev_get_ioref(c, ptr->dev, WRITE);  		if (!ca) {  			/* XXX: fix this */ -			bch_err(c, "missing device for journal write\n"); +			bch_err(c, "missing device %u for journal write", ptr->dev);  			continue;  		} diff --git a/fs/bcachefs/namei.c b/fs/bcachefs/namei.c index 46f3c8b100a9..52c58c6d53d2 100644 --- a/fs/bcachefs/namei.c +++ b/fs/bcachefs/namei.c @@ -343,6 +343,9 @@ bool bch2_reinherit_attrs(struct bch_inode_unpacked *dst_u,  	bool ret = false;  	for (id = 0; id < Inode_opt_nr; id++) { +		if (!S_ISDIR(dst_u->bi_mode) && id == Inode_opt_casefold) +			continue; +  		/* Skip attributes that were explicitly set on this inode */  		if (dst_u->bi_fields_set & (1 << id))  			continue; diff --git a/fs/bcachefs/sb-downgrade.c b/fs/bcachefs/sb-downgrade.c index acb5d845841e..badd0e17ada5 100644 --- a/fs/bcachefs/sb-downgrade.c +++ b/fs/bcachefs/sb-downgrade.c @@ -20,6 +20,10 @@   * x(version, recovery_passes, errors...)   */  #define UPGRADE_TABLE()						\ +	x(snapshot_2,						\ +	  RECOVERY_PASS_ALL_FSCK,				\ +	  BCH_FSCK_ERR_subvol_root_wrong_bi_subvol,		\ +	  BCH_FSCK_ERR_subvol_not_master_and_not_snapshot)	\  	x(backpointers,						\  	  RECOVERY_PASS_ALL_FSCK)				\  	x(inode_v3,						\ diff --git a/fs/bcachefs/sb-errors_format.h b/fs/bcachefs/sb-errors_format.h index dc53d25c7cbb..3b69a924086f 100644 --- a/fs/bcachefs/sb-errors_format.h +++ b/fs/bcachefs/sb-errors_format.h @@ -46,7 +46,7 @@ enum bch_fsck_flags {  	x(btree_node_unsupported_version,			 34,	0)		\  	x(btree_node_bset_older_than_sb_min,			 35,	0)		\  	x(btree_node_bset_newer_than_sb,			 36,	0)		\ -	x(btree_node_data_missing,				 37,	0)		\ +	x(btree_node_data_missing,				 37,	FSCK_AUTOFIX)	\  	x(btree_node_bset_after_end,				 38,	0)		\  	x(btree_node_replicas_sectors_written_mismatch,		 39,	0)		\  	x(btree_node_replicas_data_mismatch,			 40,	0)		\ @@ -205,9 +205,9 @@ enum bch_fsck_flags {  	x(snapshot_bad_depth,					184,	0)		\  	x(snapshot_bad_skiplist,				185,	0)		\  	x(subvol_pos_bad,					186,	0)		\ -	x(subvol_not_master_and_not_snapshot,			187,	0)		\ +	x(subvol_not_master_and_not_snapshot,			187,	FSCK_AUTOFIX)	\  	x(subvol_to_missing_root,				188,	0)		\ -	x(subvol_root_wrong_bi_subvol,				189,	0)		\ +	x(subvol_root_wrong_bi_subvol,				189,	FSCK_AUTOFIX)	\  	x(bkey_in_missing_snapshot,				190,	0)		\  	x(inode_pos_inode_nonzero,				191,	0)		\  	x(inode_pos_blockdev_range,				192,	0)		\ @@ -236,6 +236,9 @@ enum bch_fsck_flags {  	x(inode_has_child_snapshots_wrong,			287,	0)		\  	x(inode_unreachable,					210,	FSCK_AUTOFIX)	\  	x(inode_journal_seq_in_future,				299,	FSCK_AUTOFIX)	\ +	x(inode_i_sectors_underflow,				312,	FSCK_AUTOFIX)	\ +	x(vfs_inode_i_blocks_underflow,				311,	FSCK_AUTOFIX)	\ +	x(vfs_inode_i_blocks_not_zero_at_truncate,		313,	FSCK_AUTOFIX)	\  	x(deleted_inode_but_clean,				211,	FSCK_AUTOFIX)	\  	x(deleted_inode_missing,				212,	FSCK_AUTOFIX)	\  	x(deleted_inode_is_dir,					213,	FSCK_AUTOFIX)	\ @@ -317,7 +320,9 @@ enum bch_fsck_flags {  	x(directory_size_mismatch,				303,	FSCK_AUTOFIX)	\  	x(dirent_cf_name_too_big,				304,	0)		\  	x(dirent_stray_data_after_cf_name,			305,	0)		\ -	x(MAX,							308,	0) +	x(rebalance_work_incorrectly_set,			309,	FSCK_AUTOFIX)	\ +	x(rebalance_work_incorrectly_unset,			310,	FSCK_AUTOFIX)	\ +	x(MAX,							314,	0)  enum bch_sb_error_id {  #define x(t, n, ...) BCH_FSCK_ERR_##t = n, diff --git a/fs/bcachefs/sb-members.c b/fs/bcachefs/sb-members.c index 116131f95815..72779912939b 100644 --- a/fs/bcachefs/sb-members.c +++ b/fs/bcachefs/sb-members.c @@ -15,9 +15,11 @@ void bch2_dev_missing(struct bch_fs *c, unsigned dev)  		bch2_fs_inconsistent(c, "pointer to nonexistent device %u", dev);  } -void bch2_dev_bucket_missing(struct bch_fs *c, struct bpos bucket) +void bch2_dev_bucket_missing(struct bch_dev *ca, u64 bucket)  { -	bch2_fs_inconsistent(c, "pointer to nonexistent bucket %llu:%llu", bucket.inode, bucket.offset); +	bch2_fs_inconsistent(ca->fs, +		"pointer to nonexistent bucket %llu on device %s (valid range %u-%llu)", +		bucket, ca->name, ca->mi.first_bucket, ca->mi.nbuckets);  }  #define x(t, n, ...) [n] = #t, diff --git a/fs/bcachefs/sb-members.h b/fs/bcachefs/sb-members.h index 06bb41a3f360..42786657522c 100644 --- a/fs/bcachefs/sb-members.h +++ b/fs/bcachefs/sb-members.h @@ -249,20 +249,23 @@ static inline struct bch_dev *bch2_dev_tryget(struct bch_fs *c, unsigned dev)  static inline struct bch_dev *bch2_dev_bucket_tryget_noerror(struct bch_fs *c, struct bpos bucket)  {  	struct bch_dev *ca = bch2_dev_tryget_noerror(c, bucket.inode); -	if (ca && !bucket_valid(ca, bucket.offset)) { +	if (ca && unlikely(!bucket_valid(ca, bucket.offset))) {  		bch2_dev_put(ca);  		ca = NULL;  	}  	return ca;  } -void bch2_dev_bucket_missing(struct bch_fs *, struct bpos); +void bch2_dev_bucket_missing(struct bch_dev *, u64);  static inline struct bch_dev *bch2_dev_bucket_tryget(struct bch_fs *c, struct bpos bucket)  { -	struct bch_dev *ca = bch2_dev_bucket_tryget_noerror(c, bucket); -	if (!ca) -		bch2_dev_bucket_missing(c, bucket); +	struct bch_dev *ca = bch2_dev_tryget(c, bucket.inode); +	if (ca && unlikely(!bucket_valid(ca, bucket.offset))) { +		bch2_dev_bucket_missing(ca, bucket.offset); +		bch2_dev_put(ca); +		ca = NULL; +	}  	return ca;  } diff --git a/fs/bcachefs/subvolume.c b/fs/bcachefs/subvolume.c index 5537283d0bea..d0209f7658bb 100644 --- a/fs/bcachefs/subvolume.c +++ b/fs/bcachefs/subvolume.c @@ -6,6 +6,7 @@  #include "errcode.h"  #include "error.h"  #include "fs.h" +#include "recovery_passes.h"  #include "snapshot.h"  #include "subvolume.h" @@ -44,8 +45,8 @@ static int check_subvol(struct btree_trans *trans,  	ret = bch2_snapshot_lookup(trans, snapid, &snapshot);  	if (bch2_err_matches(ret, ENOENT)) -		bch_err(c, "subvolume %llu points to nonexistent snapshot %u", -			k.k->p.offset, snapid); +		return bch2_run_explicit_recovery_pass(c, +					BCH_RECOVERY_PASS_reconstruct_snapshots) ?: ret;  	if (ret)  		return ret; diff --git a/fs/bcachefs/super.c b/fs/bcachefs/super.c index e4ab0595c0ae..27943082c093 100644 --- a/fs/bcachefs/super.c +++ b/fs/bcachefs/super.c @@ -531,6 +531,10 @@ static void __bch2_fs_free(struct bch_fs *c)  	for (unsigned i = 0; i < BCH_TIME_STAT_NR; i++)  		bch2_time_stats_exit(&c->times[i]); +#ifdef CONFIG_UNICODE +	utf8_unload(c->cf_encoding); +#endif +  	bch2_find_btree_nodes_exit(&c->found_btree_nodes);  	bch2_free_pending_node_rewrites(c);  	bch2_free_fsck_errs(c); @@ -823,25 +827,6 @@ static struct bch_fs *bch2_fs_alloc(struct bch_sb *sb, struct bch_opts opts)  	if (ret)  		goto err; -#ifdef CONFIG_UNICODE -	/* Default encoding until we can potentially have more as an option. */ -	c->cf_encoding = utf8_load(BCH_FS_DEFAULT_UTF8_ENCODING); -	if (IS_ERR(c->cf_encoding)) { -		printk(KERN_ERR "Cannot load UTF-8 encoding for filesystem. Version: %u.%u.%u", -			unicode_major(BCH_FS_DEFAULT_UTF8_ENCODING), -			unicode_minor(BCH_FS_DEFAULT_UTF8_ENCODING), -			unicode_rev(BCH_FS_DEFAULT_UTF8_ENCODING)); -		ret = -EINVAL; -		goto err; -	} -#else -	if (c->sb.features & BIT_ULL(BCH_FEATURE_casefolding)) { -		printk(KERN_ERR "Cannot mount a filesystem with casefolding on a kernel without CONFIG_UNICODE\n"); -		ret = -EINVAL; -		goto err; -	} -#endif -  	pr_uuid(&name, c->sb.user_uuid.b);  	ret = name.allocation_failure ? -BCH_ERR_ENOMEM_fs_name_alloc : 0;  	if (ret) @@ -941,6 +926,29 @@ static struct bch_fs *bch2_fs_alloc(struct bch_sb *sb, struct bch_opts opts)  	if (ret)  		goto err; +#ifdef CONFIG_UNICODE +	/* Default encoding until we can potentially have more as an option. */ +	c->cf_encoding = utf8_load(BCH_FS_DEFAULT_UTF8_ENCODING); +	if (IS_ERR(c->cf_encoding)) { +		printk(KERN_ERR "Cannot load UTF-8 encoding for filesystem. Version: %u.%u.%u", +			unicode_major(BCH_FS_DEFAULT_UTF8_ENCODING), +			unicode_minor(BCH_FS_DEFAULT_UTF8_ENCODING), +			unicode_rev(BCH_FS_DEFAULT_UTF8_ENCODING)); +		ret = -EINVAL; +		goto err; +	} +	bch_info(c, "Using encoding defined by superblock: utf8-%u.%u.%u", +		 unicode_major(BCH_FS_DEFAULT_UTF8_ENCODING), +		 unicode_minor(BCH_FS_DEFAULT_UTF8_ENCODING), +		 unicode_rev(BCH_FS_DEFAULT_UTF8_ENCODING)); +#else +	if (c->sb.features & BIT_ULL(BCH_FEATURE_casefolding)) { +		printk(KERN_ERR "Cannot mount a filesystem with casefolding on a kernel without CONFIG_UNICODE\n"); +		ret = -EINVAL; +		goto err; +	} +#endif +  	for (i = 0; i < c->sb.nr_devices; i++) {  		if (!bch2_member_exists(c->disk_sb.sb, i))  			continue; diff --git a/fs/bcachefs/xattr_format.h b/fs/bcachefs/xattr_format.h index c7916011ef34..67426e33d04e 100644 --- a/fs/bcachefs/xattr_format.h +++ b/fs/bcachefs/xattr_format.h @@ -13,7 +13,13 @@ struct bch_xattr {  	__u8			x_type;  	__u8			x_name_len;  	__le16			x_val_len; -	__u8			x_name[] __counted_by(x_name_len); +	/* +	 * x_name contains the name and value counted by +	 * x_name_len + x_val_len. The introduction of +	 * __counted_by(x_name_len) caused a false positive +	 * detection of an out of bounds write. +	 */ +	__u8			x_name[];  } __packed __aligned(8);  #endif /* _BCACHEFS_XATTR_FORMAT_H */ | 
