2017-03-17 06:18:50 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
|
|
|
|
#include "bcachefs.h"
|
2020-12-17 20:08:58 +00:00
|
|
|
#include "bkey_buf.h"
|
2023-10-20 02:49:08 +00:00
|
|
|
#include "btree_cache.h"
|
2017-03-17 06:18:50 +00:00
|
|
|
#include "btree_update.h"
|
2023-04-17 01:49:12 +00:00
|
|
|
#include "buckets.h"
|
2022-03-29 19:48:45 +00:00
|
|
|
#include "darray.h"
|
2017-03-17 06:18:50 +00:00
|
|
|
#include "dirent.h"
|
|
|
|
#include "error.h"
|
2019-10-02 22:35:36 +00:00
|
|
|
#include "fs-common.h"
|
2017-03-17 06:18:50 +00:00
|
|
|
#include "fsck.h"
|
|
|
|
#include "inode.h"
|
|
|
|
#include "keylist.h"
|
2023-08-04 00:37:32 +00:00
|
|
|
#include "recovery.h"
|
2023-08-16 20:54:33 +00:00
|
|
|
#include "snapshot.h"
|
2017-03-17 06:18:50 +00:00
|
|
|
#include "super.h"
|
|
|
|
#include "xattr.h"
|
|
|
|
|
2021-04-22 01:08:49 +00:00
|
|
|
#include <linux/bsearch.h>
|
2017-03-17 06:18:50 +00:00
|
|
|
#include <linux/dcache.h> /* struct qstr */
|
|
|
|
|
2022-08-18 17:00:26 +00:00
|
|
|
/*
|
|
|
|
* XXX: this is handling transaction restarts without returning
|
|
|
|
* -BCH_ERR_transaction_restart_nested, this is not how we do things anymore:
|
|
|
|
*/
|
2021-04-20 04:15:44 +00:00
|
|
|
static s64 bch2_count_inode_sectors(struct btree_trans *trans, u64 inum,
|
|
|
|
u32 snapshot)
|
2019-03-25 19:10:15 +00:00
|
|
|
{
|
|
|
|
u64 sectors = 0;
|
|
|
|
|
2023-12-17 02:51:34 +00:00
|
|
|
int ret = for_each_btree_key_upto(trans, iter, BTREE_ID_extents,
|
|
|
|
SPOS(inum, 0, snapshot),
|
|
|
|
POS(inum, U64_MAX),
|
|
|
|
0, k, ({
|
2019-03-25 19:10:15 +00:00
|
|
|
if (bkey_extent_is_allocation(k.k))
|
|
|
|
sectors += k.k->size;
|
2023-12-17 02:51:34 +00:00
|
|
|
0;
|
|
|
|
}));
|
2019-04-17 19:49:28 +00:00
|
|
|
|
|
|
|
return ret ?: sectors;
|
2019-03-25 19:10:15 +00:00
|
|
|
}
|
|
|
|
|
2021-04-20 04:15:44 +00:00
|
|
|
static s64 bch2_count_subdirs(struct btree_trans *trans, u64 inum,
|
|
|
|
u32 snapshot)
|
|
|
|
{
|
|
|
|
u64 subdirs = 0;
|
|
|
|
|
2023-12-17 02:51:34 +00:00
|
|
|
int ret = for_each_btree_key_upto(trans, iter, BTREE_ID_dirents,
|
2023-12-17 02:46:23 +00:00
|
|
|
SPOS(inum, 0, snapshot),
|
|
|
|
POS(inum, U64_MAX),
|
2023-12-17 02:51:34 +00:00
|
|
|
0, k, ({
|
|
|
|
if (k.k->type == KEY_TYPE_dirent &&
|
|
|
|
bkey_s_c_to_dirent(k).v->d_type == DT_DIR)
|
2021-04-20 04:15:44 +00:00
|
|
|
subdirs++;
|
2023-12-17 02:51:34 +00:00
|
|
|
0;
|
|
|
|
}));
|
2021-04-20 04:15:44 +00:00
|
|
|
|
|
|
|
return ret ?: subdirs;
|
|
|
|
}
|
|
|
|
|
2023-12-11 03:52:43 +00:00
|
|
|
static int subvol_lookup(struct btree_trans *trans, u32 subvol,
|
|
|
|
u32 *snapshot, u64 *inum)
|
2021-04-20 03:31:40 +00:00
|
|
|
{
|
2021-09-30 23:46:23 +00:00
|
|
|
struct bch_subvolume s;
|
2021-04-20 03:31:40 +00:00
|
|
|
int ret;
|
|
|
|
|
2021-09-30 23:46:23 +00:00
|
|
|
ret = bch2_subvolume_get(trans, subvol, false, 0, &s);
|
2021-04-20 03:31:40 +00:00
|
|
|
|
2021-09-30 23:46:23 +00:00
|
|
|
*snapshot = le32_to_cpu(s.snapshot);
|
|
|
|
*inum = le64_to_cpu(s.inode);
|
2021-04-20 03:31:40 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2021-11-04 01:22:46 +00:00
|
|
|
static int lookup_first_inode(struct btree_trans *trans, u64 inode_nr,
|
|
|
|
struct bch_inode_unpacked *inode)
|
|
|
|
{
|
|
|
|
struct btree_iter iter;
|
|
|
|
struct bkey_s_c k;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
bch2_trans_iter_init(trans, &iter, BTREE_ID_inodes,
|
|
|
|
POS(0, inode_nr),
|
|
|
|
BTREE_ITER_ALL_SNAPSHOTS);
|
|
|
|
k = bch2_btree_iter_peek(&iter);
|
|
|
|
ret = bkey_err(k);
|
|
|
|
if (ret)
|
|
|
|
goto err;
|
|
|
|
|
2022-11-24 08:12:22 +00:00
|
|
|
if (!k.k || !bkey_eq(k.k->p, POS(0, inode_nr))) {
|
2023-05-27 23:59:59 +00:00
|
|
|
ret = -BCH_ERR_ENOENT_inode;
|
2021-11-04 01:22:46 +00:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2021-10-30 01:14:23 +00:00
|
|
|
ret = bch2_inode_unpack(k, inode);
|
2021-11-04 01:22:46 +00:00
|
|
|
err:
|
2023-09-26 20:02:06 +00:00
|
|
|
bch_err_msg(trans->c, ret, "fetching inode %llu", inode_nr);
|
2021-11-04 01:22:46 +00:00
|
|
|
bch2_trans_iter_exit(trans, &iter);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2023-12-11 03:52:43 +00:00
|
|
|
static int lookup_inode(struct btree_trans *trans, u64 inode_nr,
|
2021-04-20 02:19:18 +00:00
|
|
|
struct bch_inode_unpacked *inode,
|
|
|
|
u32 *snapshot)
|
2021-04-07 00:15:26 +00:00
|
|
|
{
|
2021-08-30 19:18:31 +00:00
|
|
|
struct btree_iter iter;
|
2021-04-07 00:15:26 +00:00
|
|
|
struct bkey_s_c k;
|
|
|
|
int ret;
|
|
|
|
|
2023-04-29 23:33:09 +00:00
|
|
|
k = bch2_bkey_get_iter(trans, &iter, BTREE_ID_inodes,
|
|
|
|
SPOS(0, inode_nr, *snapshot), 0);
|
2021-04-07 00:15:26 +00:00
|
|
|
ret = bkey_err(k);
|
|
|
|
if (ret)
|
|
|
|
goto err;
|
|
|
|
|
2021-10-30 01:14:23 +00:00
|
|
|
ret = bkey_is_inode(k.k)
|
|
|
|
? bch2_inode_unpack(k, inode)
|
2023-05-27 23:59:59 +00:00
|
|
|
: -BCH_ERR_ENOENT_inode;
|
2021-10-12 16:06:02 +00:00
|
|
|
if (!ret)
|
|
|
|
*snapshot = iter.pos.snapshot;
|
2021-04-07 00:15:26 +00:00
|
|
|
err:
|
2023-09-26 20:02:06 +00:00
|
|
|
bch_err_msg(trans->c, ret, "fetching inode %llu:%u", inode_nr, *snapshot);
|
2021-08-30 19:18:31 +00:00
|
|
|
bch2_trans_iter_exit(trans, &iter);
|
2021-04-07 00:15:26 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2021-04-20 04:15:44 +00:00
|
|
|
static int __lookup_dirent(struct btree_trans *trans,
|
|
|
|
struct bch_hash_info hash_info,
|
|
|
|
subvol_inum dir, struct qstr *name,
|
|
|
|
u64 *target, unsigned *type)
|
|
|
|
{
|
|
|
|
struct btree_iter iter;
|
|
|
|
struct bkey_s_c_dirent d;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = bch2_hash_lookup(trans, &iter, bch2_dirent_hash_desc,
|
|
|
|
&hash_info, dir, name, 0);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
d = bkey_s_c_to_dirent(bch2_btree_iter_peek_slot(&iter));
|
|
|
|
*target = le64_to_cpu(d.v->d_inum);
|
|
|
|
*type = d.v->d_type;
|
|
|
|
bch2_trans_iter_exit(trans, &iter);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-04-20 02:19:18 +00:00
|
|
|
static int __write_inode(struct btree_trans *trans,
|
|
|
|
struct bch_inode_unpacked *inode,
|
|
|
|
u32 snapshot)
|
2021-04-07 00:15:26 +00:00
|
|
|
{
|
2023-06-25 05:34:45 +00:00
|
|
|
struct bkey_inode_buf *inode_p =
|
|
|
|
bch2_trans_kmalloc(trans, sizeof(*inode_p));
|
2021-08-30 19:18:31 +00:00
|
|
|
|
2023-06-25 05:34:45 +00:00
|
|
|
if (IS_ERR(inode_p))
|
|
|
|
return PTR_ERR(inode_p);
|
2021-08-30 19:18:31 +00:00
|
|
|
|
2023-06-25 05:34:45 +00:00
|
|
|
bch2_inode_pack(inode_p, inode);
|
|
|
|
inode_p->inode.k.p.snapshot = snapshot;
|
|
|
|
|
|
|
|
return bch2_btree_insert_nonextent(trans, BTREE_ID_inodes,
|
|
|
|
&inode_p->inode.k_i,
|
|
|
|
BTREE_UPDATE_INTERNAL_SNAPSHOT_NODE);
|
2021-04-20 02:19:18 +00:00
|
|
|
}
|
|
|
|
|
2023-09-11 05:37:34 +00:00
|
|
|
static int fsck_write_inode(struct btree_trans *trans,
|
|
|
|
struct bch_inode_unpacked *inode,
|
|
|
|
u32 snapshot)
|
2021-04-20 02:19:18 +00:00
|
|
|
{
|
2023-11-28 21:36:54 +00:00
|
|
|
int ret = commit_do(trans, NULL, NULL, BCH_TRANS_COMMIT_no_enospc,
|
|
|
|
__write_inode(trans, inode, snapshot));
|
2023-12-17 03:43:41 +00:00
|
|
|
bch_err_fn(trans->c, ret);
|
2021-04-07 00:15:26 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2021-04-16 18:48:51 +00:00
|
|
|
static int __remove_dirent(struct btree_trans *trans, struct bpos pos)
|
2017-03-17 06:18:50 +00:00
|
|
|
{
|
2019-03-28 02:03:30 +00:00
|
|
|
struct bch_fs *c = trans->c;
|
2021-08-30 19:18:31 +00:00
|
|
|
struct btree_iter iter;
|
2017-03-17 06:18:50 +00:00
|
|
|
struct bch_inode_unpacked dir_inode;
|
|
|
|
struct bch_hash_info dir_hash_info;
|
|
|
|
int ret;
|
|
|
|
|
2021-11-04 01:22:46 +00:00
|
|
|
ret = lookup_first_inode(trans, pos.inode, &dir_inode);
|
2019-12-23 04:04:30 +00:00
|
|
|
if (ret)
|
2022-04-12 02:36:53 +00:00
|
|
|
goto err;
|
2017-03-17 06:18:50 +00:00
|
|
|
|
|
|
|
dir_hash_info = bch2_hash_info_init(c, &dir_inode);
|
|
|
|
|
2021-08-30 19:18:31 +00:00
|
|
|
bch2_trans_iter_init(trans, &iter, BTREE_ID_dirents, pos, BTREE_ITER_INTENT);
|
2019-12-23 04:04:30 +00:00
|
|
|
|
2021-04-16 18:48:51 +00:00
|
|
|
ret = bch2_hash_delete_at(trans, bch2_dirent_hash_desc,
|
2022-04-12 17:09:09 +00:00
|
|
|
&dir_hash_info, &iter,
|
|
|
|
BTREE_UPDATE_INTERNAL_SNAPSHOT_NODE);
|
2021-08-30 19:18:31 +00:00
|
|
|
bch2_trans_iter_exit(trans, &iter);
|
2022-04-12 02:36:53 +00:00
|
|
|
err:
|
2023-09-26 20:02:06 +00:00
|
|
|
bch_err_fn(c, ret);
|
2021-04-16 18:48:51 +00:00
|
|
|
return ret;
|
2019-12-23 04:04:30 +00:00
|
|
|
}
|
|
|
|
|
2021-04-20 02:19:18 +00:00
|
|
|
/* Get lost+found, create if it doesn't exist: */
|
2023-12-15 19:13:48 +00:00
|
|
|
static int lookup_lostfound(struct btree_trans *trans, u32 snapshot,
|
2021-04-20 02:19:18 +00:00
|
|
|
struct bch_inode_unpacked *lostfound)
|
2017-03-17 06:18:50 +00:00
|
|
|
{
|
2021-04-20 02:19:18 +00:00
|
|
|
struct bch_fs *c = trans->c;
|
|
|
|
struct qstr lostfound_str = QSTR("lost+found");
|
2021-04-20 04:15:44 +00:00
|
|
|
u64 inum = 0;
|
|
|
|
unsigned d_type = 0;
|
2021-04-20 02:19:18 +00:00
|
|
|
int ret;
|
|
|
|
|
2023-12-15 19:13:48 +00:00
|
|
|
struct bch_snapshot_tree st;
|
|
|
|
ret = bch2_snapshot_tree_lookup(trans,
|
|
|
|
bch2_snapshot_tree(c, snapshot), &st);
|
2021-04-20 04:15:44 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
2021-04-20 03:31:40 +00:00
|
|
|
|
2023-12-15 19:13:48 +00:00
|
|
|
subvol_inum root_inum = { .subvol = le32_to_cpu(st.master_subvol) };
|
|
|
|
u32 subvol_snapshot;
|
|
|
|
|
2023-12-11 03:52:43 +00:00
|
|
|
ret = subvol_lookup(trans, le32_to_cpu(st.master_subvol),
|
|
|
|
&subvol_snapshot, &root_inum.inum);
|
2023-12-15 19:13:48 +00:00
|
|
|
bch_err_msg(c, ret, "looking up root subvol");
|
2021-10-28 20:16:55 +00:00
|
|
|
if (ret)
|
2021-04-20 02:19:18 +00:00
|
|
|
return ret;
|
|
|
|
|
2023-12-15 19:13:48 +00:00
|
|
|
struct bch_inode_unpacked root_inode;
|
|
|
|
struct bch_hash_info root_hash_info;
|
2023-12-11 03:52:43 +00:00
|
|
|
ret = lookup_inode(trans, root_inum.inum, &root_inode, &snapshot);
|
|
|
|
bch_err_msg(c, ret, "looking up root inode");
|
2023-12-15 19:13:48 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
root_hash_info = bch2_hash_info_init(c, &root_inode);
|
2021-04-20 04:15:44 +00:00
|
|
|
|
2021-10-28 20:16:55 +00:00
|
|
|
ret = __lookup_dirent(trans, root_hash_info, root_inum,
|
2023-12-15 19:13:48 +00:00
|
|
|
&lostfound_str, &inum, &d_type);
|
|
|
|
if (bch2_err_matches(ret, ENOENT))
|
2021-04-20 02:19:18 +00:00
|
|
|
goto create_lostfound;
|
|
|
|
|
2023-09-26 20:02:06 +00:00
|
|
|
bch_err_fn(c, ret);
|
2021-10-28 20:16:55 +00:00
|
|
|
if (ret)
|
2021-04-20 04:15:44 +00:00
|
|
|
return ret;
|
|
|
|
|
|
|
|
if (d_type != DT_DIR) {
|
|
|
|
bch_err(c, "error looking up lost+found: not a directory");
|
2023-09-20 05:32:20 +00:00
|
|
|
return -BCH_ERR_ENOENT_not_directory;
|
2021-04-20 04:15:44 +00:00
|
|
|
}
|
|
|
|
|
2021-10-28 20:16:55 +00:00
|
|
|
/*
|
2023-07-07 06:42:28 +00:00
|
|
|
* The bch2_check_dirents pass has already run, dangling dirents
|
2021-10-28 20:16:55 +00:00
|
|
|
* shouldn't exist here:
|
|
|
|
*/
|
2023-12-11 03:52:43 +00:00
|
|
|
return lookup_inode(trans, inum, lostfound, &snapshot);
|
2021-04-20 02:19:18 +00:00
|
|
|
|
|
|
|
create_lostfound:
|
2023-12-15 19:13:48 +00:00
|
|
|
/*
|
|
|
|
* XXX: we could have a nicer log message here if we had a nice way to
|
|
|
|
* walk backpointers to print a path
|
|
|
|
*/
|
|
|
|
bch_notice(c, "creating lost+found in snapshot %u", le32_to_cpu(st.root_snapshot));
|
|
|
|
|
|
|
|
u64 now = bch2_current_time(c);
|
|
|
|
struct btree_iter lostfound_iter = { NULL };
|
|
|
|
u64 cpu = raw_smp_processor_id();
|
|
|
|
|
2021-10-28 20:16:55 +00:00
|
|
|
bch2_inode_init_early(c, lostfound);
|
2023-12-15 19:13:48 +00:00
|
|
|
bch2_inode_init_late(lostfound, now, 0, 0, S_IFDIR|0700, 0, &root_inode);
|
|
|
|
lostfound->bi_dir = root_inode.bi_inum;
|
|
|
|
|
|
|
|
root_inode.bi_nlink++;
|
|
|
|
|
|
|
|
ret = bch2_inode_create(trans, &lostfound_iter, lostfound, snapshot, cpu);
|
|
|
|
if (ret)
|
|
|
|
goto err;
|
2021-10-28 20:16:55 +00:00
|
|
|
|
2023-12-15 19:13:48 +00:00
|
|
|
bch2_btree_iter_set_snapshot(&lostfound_iter, snapshot);
|
|
|
|
ret = bch2_btree_iter_traverse(&lostfound_iter);
|
|
|
|
if (ret)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
ret = bch2_dirent_create_snapshot(trans,
|
|
|
|
root_inode.bi_inum, snapshot, &root_hash_info,
|
|
|
|
mode_to_type(lostfound->bi_mode),
|
|
|
|
&lostfound_str,
|
|
|
|
lostfound->bi_inum,
|
|
|
|
&lostfound->bi_dir_offset,
|
|
|
|
BCH_HASH_SET_MUST_CREATE) ?:
|
|
|
|
bch2_inode_write_flags(trans, &lostfound_iter, lostfound,
|
|
|
|
BTREE_UPDATE_INTERNAL_SNAPSHOT_NODE);
|
|
|
|
err:
|
2023-09-26 20:02:06 +00:00
|
|
|
bch_err_msg(c, ret, "creating lost+found");
|
2023-12-15 19:13:48 +00:00
|
|
|
bch2_trans_iter_exit(trans, &lostfound_iter);
|
2021-10-28 20:16:55 +00:00
|
|
|
return ret;
|
2021-04-20 02:19:18 +00:00
|
|
|
}
|
|
|
|
|
2023-12-11 03:52:43 +00:00
|
|
|
static int reattach_inode(struct btree_trans *trans,
|
2021-04-20 03:31:40 +00:00
|
|
|
struct bch_inode_unpacked *inode,
|
2021-04-20 04:15:44 +00:00
|
|
|
u32 inode_snapshot)
|
2021-04-20 02:19:18 +00:00
|
|
|
{
|
|
|
|
struct bch_hash_info dir_hash;
|
|
|
|
struct bch_inode_unpacked lostfound;
|
2017-03-17 06:18:50 +00:00
|
|
|
char name_buf[20];
|
|
|
|
struct qstr name;
|
2021-04-09 07:25:37 +00:00
|
|
|
u64 dir_offset = 0;
|
2017-03-17 06:18:50 +00:00
|
|
|
int ret;
|
|
|
|
|
2023-12-15 19:13:48 +00:00
|
|
|
ret = lookup_lostfound(trans, inode_snapshot, &lostfound);
|
2021-04-09 07:25:37 +00:00
|
|
|
if (ret)
|
2021-04-07 07:11:07 +00:00
|
|
|
return ret;
|
2021-04-09 07:25:37 +00:00
|
|
|
|
2021-04-20 02:19:18 +00:00
|
|
|
if (S_ISDIR(inode->bi_mode)) {
|
|
|
|
lostfound.bi_nlink++;
|
2021-04-09 07:25:37 +00:00
|
|
|
|
2021-10-28 20:16:55 +00:00
|
|
|
ret = __write_inode(trans, &lostfound, U32_MAX);
|
2021-04-09 07:25:37 +00:00
|
|
|
if (ret)
|
2021-04-07 07:11:07 +00:00
|
|
|
return ret;
|
2021-04-09 07:25:37 +00:00
|
|
|
}
|
|
|
|
|
2021-04-20 02:19:18 +00:00
|
|
|
dir_hash = bch2_hash_info_init(trans->c, &lostfound);
|
2021-04-09 07:25:37 +00:00
|
|
|
|
2021-04-20 02:19:18 +00:00
|
|
|
snprintf(name_buf, sizeof(name_buf), "%llu", inode->bi_inum);
|
|
|
|
name = (struct qstr) QSTR(name_buf);
|
2021-04-09 07:25:37 +00:00
|
|
|
|
2023-12-15 19:13:48 +00:00
|
|
|
ret = bch2_dirent_create_snapshot(trans,
|
|
|
|
lostfound.bi_inum, inode_snapshot,
|
|
|
|
&dir_hash,
|
|
|
|
inode_d_type(inode),
|
|
|
|
&name, inode->bi_inum, &dir_offset,
|
|
|
|
BCH_HASH_SET_MUST_CREATE);
|
2021-10-28 20:16:55 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
inode->bi_dir = lostfound.bi_inum;
|
|
|
|
inode->bi_dir_offset = dir_offset;
|
|
|
|
|
|
|
|
return __write_inode(trans, inode, inode_snapshot);
|
|
|
|
}
|
|
|
|
|
2021-04-07 07:11:07 +00:00
|
|
|
static int remove_backpointer(struct btree_trans *trans,
|
|
|
|
struct bch_inode_unpacked *inode)
|
|
|
|
{
|
2021-08-30 19:18:31 +00:00
|
|
|
struct btree_iter iter;
|
2023-04-29 23:33:09 +00:00
|
|
|
struct bkey_s_c_dirent d;
|
2021-04-07 07:11:07 +00:00
|
|
|
int ret;
|
|
|
|
|
2023-04-29 23:33:09 +00:00
|
|
|
d = bch2_bkey_get_iter_typed(trans, &iter, BTREE_ID_dirents,
|
|
|
|
POS(inode->bi_dir, inode->bi_dir_offset), 0,
|
|
|
|
dirent);
|
|
|
|
ret = bkey_err(d) ?:
|
|
|
|
__remove_dirent(trans, d.k->p);
|
2021-08-30 19:18:31 +00:00
|
|
|
bch2_trans_iter_exit(trans, &iter);
|
2021-04-07 07:11:07 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2022-07-14 06:47:36 +00:00
|
|
|
struct snapshots_seen_entry {
|
|
|
|
u32 id;
|
|
|
|
u32 equiv;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct snapshots_seen {
|
|
|
|
struct bpos pos;
|
|
|
|
DARRAY(struct snapshots_seen_entry) ids;
|
|
|
|
};
|
|
|
|
|
|
|
|
static inline void snapshots_seen_exit(struct snapshots_seen *s)
|
|
|
|
{
|
|
|
|
darray_exit(&s->ids);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void snapshots_seen_init(struct snapshots_seen *s)
|
|
|
|
{
|
|
|
|
memset(s, 0, sizeof(*s));
|
|
|
|
}
|
|
|
|
|
2023-07-21 02:42:26 +00:00
|
|
|
static int snapshots_seen_add_inorder(struct bch_fs *c, struct snapshots_seen *s, u32 id)
|
|
|
|
{
|
|
|
|
struct snapshots_seen_entry *i, n = {
|
|
|
|
.id = id,
|
|
|
|
.equiv = bch2_snapshot_equiv(c, id),
|
|
|
|
};
|
|
|
|
int ret = 0;
|
|
|
|
|
2023-12-17 02:40:26 +00:00
|
|
|
__darray_for_each(s->ids, i) {
|
2023-07-21 02:42:26 +00:00
|
|
|
if (i->id == id)
|
|
|
|
return 0;
|
|
|
|
if (i->id > id)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = darray_insert_item(&s->ids, i - s->ids.data, n);
|
|
|
|
if (ret)
|
|
|
|
bch_err(c, "error reallocating snapshots_seen table (size %zu)",
|
|
|
|
s->ids.size);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2022-07-14 06:47:36 +00:00
|
|
|
static int snapshots_seen_update(struct bch_fs *c, struct snapshots_seen *s,
|
|
|
|
enum btree_id btree_id, struct bpos pos)
|
2021-04-20 04:15:44 +00:00
|
|
|
{
|
2023-12-17 02:40:26 +00:00
|
|
|
struct snapshots_seen_entry n = {
|
2022-07-14 06:47:36 +00:00
|
|
|
.id = pos.snapshot,
|
|
|
|
.equiv = bch2_snapshot_equiv(c, pos.snapshot),
|
|
|
|
};
|
2022-08-11 00:22:01 +00:00
|
|
|
int ret = 0;
|
2021-04-20 04:15:44 +00:00
|
|
|
|
2022-11-24 08:12:22 +00:00
|
|
|
if (!bkey_eq(s->pos, pos))
|
2022-03-29 19:48:45 +00:00
|
|
|
s->ids.nr = 0;
|
2022-07-14 06:47:36 +00:00
|
|
|
|
2021-04-20 04:15:44 +00:00
|
|
|
s->pos = pos;
|
2023-07-17 01:09:37 +00:00
|
|
|
s->pos.snapshot = n.equiv;
|
2021-04-20 04:15:44 +00:00
|
|
|
|
2023-07-17 01:09:37 +00:00
|
|
|
darray_for_each(s->ids, i) {
|
|
|
|
if (i->id == n.id)
|
2022-07-14 06:47:36 +00:00
|
|
|
return 0;
|
2023-07-17 01:09:37 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We currently don't rigorously track for snapshot cleanup
|
|
|
|
* needing to be run, so it shouldn't be a fsck error yet:
|
|
|
|
*/
|
|
|
|
if (i->equiv == n.equiv) {
|
|
|
|
bch_err(c, "snapshot deletion did not finish:\n"
|
|
|
|
" duplicate keys in btree %s at %llu:%llu snapshots %u, %u (equiv %u)\n",
|
2023-10-20 02:49:08 +00:00
|
|
|
bch2_btree_id_str(btree_id),
|
2023-07-17 01:09:37 +00:00
|
|
|
pos.inode, pos.offset,
|
|
|
|
i->id, n.id, n.equiv);
|
2023-11-26 22:05:02 +00:00
|
|
|
set_bit(BCH_FS_need_delete_dead_snapshots, &c->flags);
|
2023-07-17 03:21:17 +00:00
|
|
|
return bch2_run_explicit_recovery_pass(c, BCH_RECOVERY_PASS_delete_dead_snapshots);
|
2022-07-14 06:47:36 +00:00
|
|
|
}
|
2023-07-17 01:09:37 +00:00
|
|
|
}
|
2022-07-14 06:47:36 +00:00
|
|
|
|
|
|
|
ret = darray_push(&s->ids, n);
|
|
|
|
if (ret)
|
|
|
|
bch_err(c, "error reallocating snapshots_seen table (size %zu)",
|
|
|
|
s->ids.size);
|
|
|
|
return ret;
|
2021-04-20 04:15:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* key_visible_in_snapshot - returns true if @id is a descendent of @ancestor,
|
|
|
|
* and @ancestor hasn't been overwritten in @seen
|
|
|
|
*
|
2023-09-12 22:41:22 +00:00
|
|
|
* @c: filesystem handle
|
|
|
|
* @seen: list of snapshot ids already seen at current position
|
|
|
|
* @id: descendent snapshot id
|
|
|
|
* @ancestor: ancestor snapshot id
|
|
|
|
*
|
|
|
|
* Returns: whether key in @ancestor snapshot is visible in @id snapshot
|
2021-04-20 04:15:44 +00:00
|
|
|
*/
|
|
|
|
static bool key_visible_in_snapshot(struct bch_fs *c, struct snapshots_seen *seen,
|
|
|
|
u32 id, u32 ancestor)
|
|
|
|
{
|
|
|
|
ssize_t i;
|
|
|
|
|
2023-07-16 22:15:01 +00:00
|
|
|
EBUG_ON(id > ancestor);
|
|
|
|
EBUG_ON(!bch2_snapshot_is_equiv(c, id));
|
|
|
|
EBUG_ON(!bch2_snapshot_is_equiv(c, ancestor));
|
2021-04-20 04:15:44 +00:00
|
|
|
|
|
|
|
/* @ancestor should be the snapshot most recently added to @seen */
|
2023-07-16 22:15:01 +00:00
|
|
|
EBUG_ON(ancestor != seen->pos.snapshot);
|
|
|
|
EBUG_ON(ancestor != seen->ids.data[seen->ids.nr - 1].equiv);
|
2021-04-20 04:15:44 +00:00
|
|
|
|
|
|
|
if (id == ancestor)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (!bch2_snapshot_is_ancestor(c, id, ancestor))
|
|
|
|
return false;
|
|
|
|
|
2023-07-16 22:15:01 +00:00
|
|
|
/*
|
|
|
|
* We know that @id is a descendant of @ancestor, we're checking if
|
|
|
|
* we've seen a key that overwrote @ancestor - i.e. also a descendent of
|
|
|
|
* @ascestor and with @id as a descendent.
|
|
|
|
*
|
|
|
|
* But we already know that we're scanning IDs between @id and @ancestor
|
|
|
|
* numerically, since snapshot ID lists are kept sorted, so if we find
|
|
|
|
* an id that's an ancestor of @id we're done:
|
|
|
|
*/
|
|
|
|
|
2022-03-29 19:48:45 +00:00
|
|
|
for (i = seen->ids.nr - 2;
|
2022-07-14 06:47:36 +00:00
|
|
|
i >= 0 && seen->ids.data[i].equiv >= id;
|
2021-04-20 04:15:44 +00:00
|
|
|
--i)
|
2023-07-16 22:15:01 +00:00
|
|
|
if (bch2_snapshot_is_ancestor(c, id, seen->ids.data[i].equiv))
|
2021-04-20 04:15:44 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ref_visible - given a key with snapshot id @src that points to a key with
|
|
|
|
* snapshot id @dst, test whether there is some snapshot in which @dst is
|
|
|
|
* visible.
|
|
|
|
*
|
2023-09-12 22:41:22 +00:00
|
|
|
* @c: filesystem handle
|
|
|
|
* @s: list of snapshot IDs already seen at @src
|
|
|
|
* @src: snapshot ID of src key
|
|
|
|
* @dst: snapshot ID of dst key
|
|
|
|
* Returns: true if there is some snapshot in which @dst is visible
|
2021-04-20 04:15:44 +00:00
|
|
|
*
|
2023-09-12 22:41:22 +00:00
|
|
|
* Assumes we're visiting @src keys in natural key order
|
2021-04-20 04:15:44 +00:00
|
|
|
*/
|
2023-09-12 22:41:22 +00:00
|
|
|
static bool ref_visible(struct bch_fs *c, struct snapshots_seen *s,
|
|
|
|
u32 src, u32 dst)
|
2021-04-20 04:15:44 +00:00
|
|
|
{
|
|
|
|
return dst <= src
|
|
|
|
? key_visible_in_snapshot(c, s, dst, src)
|
|
|
|
: bch2_snapshot_is_ancestor(c, src, dst);
|
|
|
|
}
|
|
|
|
|
2022-05-08 03:03:28 +00:00
|
|
|
static int ref_visible2(struct bch_fs *c,
|
|
|
|
u32 src, struct snapshots_seen *src_seen,
|
|
|
|
u32 dst, struct snapshots_seen *dst_seen)
|
|
|
|
{
|
|
|
|
src = bch2_snapshot_equiv(c, src);
|
|
|
|
dst = bch2_snapshot_equiv(c, dst);
|
|
|
|
|
|
|
|
if (dst > src) {
|
|
|
|
swap(dst, src);
|
|
|
|
swap(dst_seen, src_seen);
|
|
|
|
}
|
|
|
|
return key_visible_in_snapshot(c, src_seen, dst, src);
|
|
|
|
}
|
|
|
|
|
2022-07-14 06:47:36 +00:00
|
|
|
#define for_each_visible_inode(_c, _s, _w, _snapshot, _i) \
|
|
|
|
for (_i = (_w)->inodes.data; _i < (_w)->inodes.data + (_w)->inodes.nr && \
|
|
|
|
(_i)->snapshot <= (_snapshot); _i++) \
|
2021-04-20 04:15:44 +00:00
|
|
|
if (key_visible_in_snapshot(_c, _s, _i->snapshot, _snapshot))
|
|
|
|
|
2022-03-29 19:48:45 +00:00
|
|
|
struct inode_walker_entry {
|
|
|
|
struct bch_inode_unpacked inode;
|
|
|
|
u32 snapshot;
|
2023-07-16 18:24:36 +00:00
|
|
|
bool seen_this_pos;
|
2022-03-29 19:48:45 +00:00
|
|
|
u64 count;
|
|
|
|
};
|
|
|
|
|
2017-03-17 06:18:50 +00:00
|
|
|
struct inode_walker {
|
2021-04-20 04:15:44 +00:00
|
|
|
bool first_this_inode;
|
2023-07-13 07:11:16 +00:00
|
|
|
bool recalculate_sums;
|
2023-07-16 18:24:36 +00:00
|
|
|
struct bpos last_pos;
|
2021-04-20 04:15:44 +00:00
|
|
|
|
2022-03-29 19:48:45 +00:00
|
|
|
DARRAY(struct inode_walker_entry) inodes;
|
2017-03-17 06:18:50 +00:00
|
|
|
};
|
|
|
|
|
2021-04-20 04:15:44 +00:00
|
|
|
static void inode_walker_exit(struct inode_walker *w)
|
|
|
|
{
|
2022-03-29 19:48:45 +00:00
|
|
|
darray_exit(&w->inodes);
|
2021-04-20 04:15:44 +00:00
|
|
|
}
|
|
|
|
|
2017-03-17 06:18:50 +00:00
|
|
|
static struct inode_walker inode_walker_init(void)
|
|
|
|
{
|
2021-04-20 04:15:44 +00:00
|
|
|
return (struct inode_walker) { 0, };
|
|
|
|
}
|
|
|
|
|
|
|
|
static int add_inode(struct bch_fs *c, struct inode_walker *w,
|
2021-10-30 01:14:23 +00:00
|
|
|
struct bkey_s_c inode)
|
2021-04-20 04:15:44 +00:00
|
|
|
{
|
|
|
|
struct bch_inode_unpacked u;
|
|
|
|
|
|
|
|
BUG_ON(bch2_inode_unpack(inode, &u));
|
|
|
|
|
2022-03-29 19:48:45 +00:00
|
|
|
return darray_push(&w->inodes, ((struct inode_walker_entry) {
|
2021-04-20 04:15:44 +00:00
|
|
|
.inode = u,
|
2022-07-14 06:47:36 +00:00
|
|
|
.snapshot = bch2_snapshot_equiv(c, inode.k->p.snapshot),
|
2022-03-29 19:48:45 +00:00
|
|
|
}));
|
2017-03-17 06:18:50 +00:00
|
|
|
}
|
|
|
|
|
2023-06-25 20:35:49 +00:00
|
|
|
static int get_inodes_all_snapshots(struct btree_trans *trans,
|
|
|
|
struct inode_walker *w, u64 inum)
|
2017-03-17 06:18:50 +00:00
|
|
|
{
|
2021-04-20 04:15:44 +00:00
|
|
|
struct bch_fs *c = trans->c;
|
|
|
|
struct btree_iter iter;
|
|
|
|
struct bkey_s_c k;
|
|
|
|
int ret;
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2023-07-13 07:11:16 +00:00
|
|
|
w->recalculate_sums = false;
|
2022-03-29 19:48:45 +00:00
|
|
|
w->inodes.nr = 0;
|
2021-04-20 04:15:44 +00:00
|
|
|
|
2023-12-08 04:28:26 +00:00
|
|
|
for_each_btree_key_norestart(trans, iter, BTREE_ID_inodes, POS(0, inum),
|
|
|
|
BTREE_ITER_ALL_SNAPSHOTS, k, ret) {
|
2023-06-25 20:35:49 +00:00
|
|
|
if (k.k->p.offset != inum)
|
2021-04-20 04:15:44 +00:00
|
|
|
break;
|
|
|
|
|
2021-10-30 01:14:23 +00:00
|
|
|
if (bkey_is_inode(k.k))
|
|
|
|
add_inode(c, w, k);
|
2021-04-20 04:15:44 +00:00
|
|
|
}
|
|
|
|
bch2_trans_iter_exit(trans, &iter);
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2023-07-16 18:24:36 +00:00
|
|
|
w->first_this_inode = true;
|
2023-12-08 04:28:26 +00:00
|
|
|
return 0;
|
2023-06-25 20:35:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct inode_walker_entry *
|
2023-07-16 18:19:08 +00:00
|
|
|
lookup_inode_for_snapshot(struct bch_fs *c, struct inode_walker *w,
|
|
|
|
u32 snapshot, bool is_whiteout)
|
2023-06-25 20:35:49 +00:00
|
|
|
{
|
|
|
|
struct inode_walker_entry *i;
|
|
|
|
|
|
|
|
snapshot = bch2_snapshot_equiv(c, snapshot);
|
|
|
|
|
2023-12-17 02:40:26 +00:00
|
|
|
__darray_for_each(w->inodes, i)
|
2023-06-25 20:35:49 +00:00
|
|
|
if (bch2_snapshot_is_ancestor(c, snapshot, i->snapshot))
|
2021-04-20 04:15:44 +00:00
|
|
|
goto found;
|
2023-06-25 20:35:49 +00:00
|
|
|
|
|
|
|
return NULL;
|
2021-04-20 04:15:44 +00:00
|
|
|
found:
|
2023-06-25 20:35:49 +00:00
|
|
|
BUG_ON(snapshot > i->snapshot);
|
2021-04-20 04:15:44 +00:00
|
|
|
|
2023-07-16 18:19:08 +00:00
|
|
|
if (snapshot != i->snapshot && !is_whiteout) {
|
2023-06-25 20:35:49 +00:00
|
|
|
struct inode_walker_entry new = *i;
|
2023-07-17 01:56:18 +00:00
|
|
|
size_t pos;
|
2023-06-25 20:35:49 +00:00
|
|
|
int ret;
|
2022-07-14 06:47:36 +00:00
|
|
|
|
2023-06-25 20:35:49 +00:00
|
|
|
new.snapshot = snapshot;
|
|
|
|
new.count = 0;
|
2022-07-14 06:47:36 +00:00
|
|
|
|
|
|
|
bch_info(c, "have key for inode %llu:%u but have inode in ancestor snapshot %u",
|
2023-07-16 18:24:36 +00:00
|
|
|
w->last_pos.inode, snapshot, i->snapshot);
|
2021-04-20 04:15:44 +00:00
|
|
|
|
2023-06-25 20:35:49 +00:00
|
|
|
while (i > w->inodes.data && i[-1].snapshot > snapshot)
|
2021-04-20 04:15:44 +00:00
|
|
|
--i;
|
|
|
|
|
2023-07-17 01:56:18 +00:00
|
|
|
pos = i - w->inodes.data;
|
|
|
|
ret = darray_insert_item(&w->inodes, pos, new);
|
2021-04-20 04:15:44 +00:00
|
|
|
if (ret)
|
2023-06-25 20:35:49 +00:00
|
|
|
return ERR_PTR(ret);
|
2023-07-17 01:56:18 +00:00
|
|
|
|
|
|
|
i = w->inodes.data + pos;
|
2021-04-20 04:15:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return i;
|
2017-03-17 06:18:50 +00:00
|
|
|
}
|
|
|
|
|
2023-06-25 20:35:49 +00:00
|
|
|
static struct inode_walker_entry *walk_inode(struct btree_trans *trans,
|
2023-07-16 18:19:08 +00:00
|
|
|
struct inode_walker *w, struct bpos pos,
|
|
|
|
bool is_whiteout)
|
2023-06-25 20:35:49 +00:00
|
|
|
{
|
2023-07-16 18:24:36 +00:00
|
|
|
if (w->last_pos.inode != pos.inode) {
|
|
|
|
int ret = get_inodes_all_snapshots(trans, w, pos.inode);
|
|
|
|
if (ret)
|
|
|
|
return ERR_PTR(ret);
|
|
|
|
} else if (bkey_cmp(w->last_pos, pos)) {
|
|
|
|
darray_for_each(w->inodes, i)
|
|
|
|
i->seen_this_pos = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
w->last_pos = pos;
|
2023-06-25 20:35:49 +00:00
|
|
|
|
2023-07-16 18:19:08 +00:00
|
|
|
return lookup_inode_for_snapshot(trans->c, w, pos.snapshot, is_whiteout);
|
2023-06-25 20:35:49 +00:00
|
|
|
}
|
|
|
|
|
2021-04-20 04:15:44 +00:00
|
|
|
static int __get_visible_inodes(struct btree_trans *trans,
|
|
|
|
struct inode_walker *w,
|
|
|
|
struct snapshots_seen *s,
|
|
|
|
u64 inum)
|
|
|
|
{
|
|
|
|
struct bch_fs *c = trans->c;
|
|
|
|
struct btree_iter iter;
|
|
|
|
struct bkey_s_c k;
|
|
|
|
int ret;
|
|
|
|
|
2022-03-29 19:48:45 +00:00
|
|
|
w->inodes.nr = 0;
|
2021-04-20 04:15:44 +00:00
|
|
|
|
2022-08-18 21:00:12 +00:00
|
|
|
for_each_btree_key_norestart(trans, iter, BTREE_ID_inodes, POS(0, inum),
|
2021-04-20 04:15:44 +00:00
|
|
|
BTREE_ITER_ALL_SNAPSHOTS, k, ret) {
|
2022-07-14 06:47:36 +00:00
|
|
|
u32 equiv = bch2_snapshot_equiv(c, k.k->p.snapshot);
|
|
|
|
|
2021-04-20 04:15:44 +00:00
|
|
|
if (k.k->p.offset != inum)
|
|
|
|
break;
|
|
|
|
|
2022-07-14 06:47:36 +00:00
|
|
|
if (!ref_visible(c, s, s->pos.snapshot, equiv))
|
2021-04-20 04:15:44 +00:00
|
|
|
continue;
|
|
|
|
|
2022-07-14 06:47:36 +00:00
|
|
|
if (bkey_is_inode(k.k))
|
2021-10-30 01:14:23 +00:00
|
|
|
add_inode(c, w, k);
|
2022-07-14 06:47:36 +00:00
|
|
|
|
|
|
|
if (equiv >= s->pos.snapshot)
|
|
|
|
break;
|
2021-04-20 04:15:44 +00:00
|
|
|
}
|
|
|
|
bch2_trans_iter_exit(trans, &iter);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int check_key_has_snapshot(struct btree_trans *trans,
|
|
|
|
struct btree_iter *iter,
|
|
|
|
struct bkey_s_c k)
|
|
|
|
{
|
|
|
|
struct bch_fs *c = trans->c;
|
2022-02-25 18:18:19 +00:00
|
|
|
struct printbuf buf = PRINTBUF;
|
2021-04-20 04:15:44 +00:00
|
|
|
int ret = 0;
|
|
|
|
|
2022-07-14 06:47:36 +00:00
|
|
|
if (mustfix_fsck_err_on(!bch2_snapshot_equiv(c, k.k->p.snapshot), c,
|
2023-10-25 00:44:36 +00:00
|
|
|
bkey_in_missing_snapshot,
|
|
|
|
"key in missing snapshot: %s",
|
|
|
|
(bch2_bkey_val_to_text(&buf, c, k), buf.buf)))
|
2022-02-25 18:18:19 +00:00
|
|
|
ret = bch2_btree_delete_at(trans, iter,
|
2021-10-28 20:16:55 +00:00
|
|
|
BTREE_UPDATE_INTERNAL_SNAPSHOT_NODE) ?: 1;
|
2021-04-20 04:15:44 +00:00
|
|
|
fsck_err:
|
2022-02-25 18:18:19 +00:00
|
|
|
printbuf_exit(&buf);
|
2021-04-20 04:15:44 +00:00
|
|
|
return ret;
|
2021-07-15 00:28:27 +00:00
|
|
|
}
|
|
|
|
|
2021-04-07 05:55:57 +00:00
|
|
|
static int hash_redo_key(struct btree_trans *trans,
|
|
|
|
const struct bch_hash_desc desc,
|
|
|
|
struct bch_hash_info *hash_info,
|
|
|
|
struct btree_iter *k_iter, struct bkey_s_c k)
|
2017-03-17 06:18:50 +00:00
|
|
|
{
|
2021-04-25 02:33:25 +00:00
|
|
|
struct bkey_i *delete;
|
2017-03-17 06:18:50 +00:00
|
|
|
struct bkey_i *tmp;
|
|
|
|
|
2021-04-25 02:33:25 +00:00
|
|
|
delete = bch2_trans_kmalloc(trans, sizeof(*delete));
|
|
|
|
if (IS_ERR(delete))
|
|
|
|
return PTR_ERR(delete);
|
|
|
|
|
2023-04-30 23:21:06 +00:00
|
|
|
tmp = bch2_bkey_make_mut_noupdate(trans, k);
|
2019-12-23 04:04:30 +00:00
|
|
|
if (IS_ERR(tmp))
|
|
|
|
return PTR_ERR(tmp);
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2021-04-25 02:33:25 +00:00
|
|
|
bkey_init(&delete->k);
|
|
|
|
delete->k.p = k_iter->pos;
|
2021-06-14 22:16:10 +00:00
|
|
|
return bch2_btree_iter_traverse(k_iter) ?:
|
|
|
|
bch2_trans_update(trans, k_iter, delete, 0) ?:
|
2022-09-04 18:10:12 +00:00
|
|
|
bch2_hash_set_snapshot(trans, desc, hash_info,
|
|
|
|
(subvol_inum) { 0, k.k->p.inode },
|
|
|
|
k.k->p.snapshot, tmp,
|
|
|
|
BCH_HASH_SET_MUST_CREATE,
|
|
|
|
BTREE_UPDATE_INTERNAL_SNAPSHOT_NODE) ?:
|
2023-11-28 21:36:54 +00:00
|
|
|
bch2_trans_commit(trans, NULL, NULL, BCH_TRANS_COMMIT_no_enospc);
|
2017-03-17 06:18:50 +00:00
|
|
|
}
|
|
|
|
|
2021-04-07 05:55:57 +00:00
|
|
|
static int hash_check_key(struct btree_trans *trans,
|
|
|
|
const struct bch_hash_desc desc,
|
|
|
|
struct bch_hash_info *hash_info,
|
|
|
|
struct btree_iter *k_iter, struct bkey_s_c hash_k)
|
2018-07-12 23:19:41 +00:00
|
|
|
{
|
2019-03-25 19:10:15 +00:00
|
|
|
struct bch_fs *c = trans->c;
|
2021-08-30 19:18:31 +00:00
|
|
|
struct btree_iter iter = { NULL };
|
2022-02-25 18:18:19 +00:00
|
|
|
struct printbuf buf = PRINTBUF;
|
2021-04-07 05:55:57 +00:00
|
|
|
struct bkey_s_c k;
|
|
|
|
u64 hash;
|
2018-07-12 23:19:41 +00:00
|
|
|
int ret = 0;
|
|
|
|
|
2021-04-07 05:55:57 +00:00
|
|
|
if (hash_k.k->type != desc.key_type)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
hash = desc.hash_bkey(hash_info, hash_k);
|
|
|
|
|
|
|
|
if (likely(hash == hash_k.k->p.offset))
|
2018-07-12 23:19:41 +00:00
|
|
|
return 0;
|
|
|
|
|
2021-04-07 05:55:57 +00:00
|
|
|
if (hash_k.k->p.offset < hash)
|
|
|
|
goto bad_hash;
|
2018-07-12 23:19:41 +00:00
|
|
|
|
2022-04-17 23:02:04 +00:00
|
|
|
for_each_btree_key_norestart(trans, iter, desc.btree_id,
|
2023-02-01 21:46:42 +00:00
|
|
|
SPOS(hash_k.k->p.inode, hash, hash_k.k->p.snapshot),
|
2022-04-17 23:02:04 +00:00
|
|
|
BTREE_ITER_SLOTS, k, ret) {
|
2022-11-24 08:12:22 +00:00
|
|
|
if (bkey_eq(k.k->p, hash_k.k->p))
|
2018-07-12 23:19:41 +00:00
|
|
|
break;
|
|
|
|
|
2021-04-07 05:55:57 +00:00
|
|
|
if (fsck_err_on(k.k->type == desc.key_type &&
|
|
|
|
!desc.cmp_bkey(k, hash_k), c,
|
2023-10-25 00:44:36 +00:00
|
|
|
hash_table_key_duplicate,
|
2018-07-12 23:19:41 +00:00
|
|
|
"duplicate hash table keys:\n%s",
|
2022-02-25 18:18:19 +00:00
|
|
|
(printbuf_reset(&buf),
|
|
|
|
bch2_bkey_val_to_text(&buf, c, hash_k),
|
|
|
|
buf.buf))) {
|
2021-10-28 20:16:55 +00:00
|
|
|
ret = bch2_hash_delete_at(trans, desc, hash_info, k_iter, 0) ?: 1;
|
2018-07-12 23:19:41 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-04-07 05:55:57 +00:00
|
|
|
if (bkey_deleted(k.k)) {
|
2021-08-30 19:18:31 +00:00
|
|
|
bch2_trans_iter_exit(trans, &iter);
|
2021-04-07 05:55:57 +00:00
|
|
|
goto bad_hash;
|
2017-03-17 06:18:50 +00:00
|
|
|
}
|
2021-04-07 05:55:57 +00:00
|
|
|
}
|
2022-02-25 18:18:19 +00:00
|
|
|
out:
|
2021-08-30 19:18:31 +00:00
|
|
|
bch2_trans_iter_exit(trans, &iter);
|
2022-02-25 18:18:19 +00:00
|
|
|
printbuf_exit(&buf);
|
2017-03-17 06:18:50 +00:00
|
|
|
return ret;
|
2021-04-07 05:55:57 +00:00
|
|
|
bad_hash:
|
2023-10-25 00:44:36 +00:00
|
|
|
if (fsck_err(c, hash_table_key_wrong_offset,
|
|
|
|
"hash table key at wrong offset: btree %s inode %llu offset %llu, hashed to %llu\n%s",
|
2023-10-20 02:49:08 +00:00
|
|
|
bch2_btree_id_str(desc.btree_id), hash_k.k->p.inode, hash_k.k->p.offset, hash,
|
2022-02-25 18:18:19 +00:00
|
|
|
(printbuf_reset(&buf),
|
2022-07-19 21:20:18 +00:00
|
|
|
bch2_bkey_val_to_text(&buf, c, hash_k), buf.buf))) {
|
|
|
|
ret = hash_redo_key(trans, desc, hash_info, k_iter, hash_k);
|
2023-09-26 20:02:06 +00:00
|
|
|
bch_err_fn(c, ret);
|
2023-04-25 18:32:39 +00:00
|
|
|
if (ret)
|
2022-07-19 21:20:18 +00:00
|
|
|
return ret;
|
|
|
|
ret = -BCH_ERR_transaction_restart_nested;
|
2018-08-21 23:42:00 +00:00
|
|
|
}
|
|
|
|
fsck_err:
|
2022-02-25 18:18:19 +00:00
|
|
|
goto out;
|
2018-08-21 23:42:00 +00:00
|
|
|
}
|
|
|
|
|
2023-11-16 22:24:54 +00:00
|
|
|
static int check_inode_deleted_list(struct btree_trans *trans, struct bpos p)
|
|
|
|
{
|
|
|
|
struct btree_iter iter;
|
|
|
|
struct bkey_s_c k = bch2_bkey_get_iter(trans, &iter, BTREE_ID_deleted_inodes, p, 0);
|
|
|
|
int ret = bkey_err(k);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
bch2_trans_iter_exit(trans, &iter);
|
|
|
|
return k.k->type == KEY_TYPE_set;
|
|
|
|
}
|
|
|
|
|
2021-04-07 01:41:48 +00:00
|
|
|
static int check_inode(struct btree_trans *trans,
|
|
|
|
struct btree_iter *iter,
|
2022-07-16 00:51:09 +00:00
|
|
|
struct bkey_s_c k,
|
2021-04-20 04:15:44 +00:00
|
|
|
struct bch_inode_unpacked *prev,
|
2022-07-14 06:47:36 +00:00
|
|
|
struct snapshots_seen *s,
|
2021-10-28 20:16:55 +00:00
|
|
|
bool full)
|
2021-04-07 01:41:48 +00:00
|
|
|
{
|
|
|
|
struct bch_fs *c = trans->c;
|
2021-10-28 20:16:55 +00:00
|
|
|
struct bch_inode_unpacked u;
|
2021-04-07 01:41:48 +00:00
|
|
|
bool do_update = false;
|
2021-10-28 20:16:55 +00:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = check_key_has_snapshot(trans, iter, k);
|
2022-04-12 02:36:53 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto err;
|
2021-10-28 20:16:55 +00:00
|
|
|
if (ret)
|
2022-04-12 02:36:53 +00:00
|
|
|
return 0;
|
2021-10-28 20:16:55 +00:00
|
|
|
|
2022-07-14 06:47:36 +00:00
|
|
|
ret = snapshots_seen_update(c, s, iter->btree_id, k.k->p);
|
|
|
|
if (ret)
|
|
|
|
goto err;
|
|
|
|
|
2021-10-30 01:14:23 +00:00
|
|
|
if (!bkey_is_inode(k.k))
|
2021-10-28 20:16:55 +00:00
|
|
|
return 0;
|
|
|
|
|
2021-10-30 01:14:23 +00:00
|
|
|
BUG_ON(bch2_inode_unpack(k, &u));
|
2021-10-28 20:16:55 +00:00
|
|
|
|
|
|
|
if (!full &&
|
2023-11-02 15:42:48 +00:00
|
|
|
!(u.bi_flags & (BCH_INODE_i_size_dirty|
|
|
|
|
BCH_INODE_i_sectors_dirty|
|
|
|
|
BCH_INODE_unlinked)))
|
2021-10-28 20:16:55 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (prev->bi_inum != u.bi_inum)
|
|
|
|
*prev = u;
|
|
|
|
|
|
|
|
if (fsck_err_on(prev->bi_hash_seed != u.bi_hash_seed ||
|
2023-10-25 00:44:36 +00:00
|
|
|
inode_d_type(prev) != inode_d_type(&u),
|
|
|
|
c, inode_snapshot_mismatch,
|
2021-04-20 04:15:44 +00:00
|
|
|
"inodes in different snapshots don't match")) {
|
|
|
|
bch_err(c, "repair not implemented yet");
|
2023-11-16 22:24:54 +00:00
|
|
|
return -BCH_ERR_fsck_repair_unimplemented;
|
2021-04-20 04:15:44 +00:00
|
|
|
}
|
2021-04-07 01:41:48 +00:00
|
|
|
|
2023-11-02 15:42:48 +00:00
|
|
|
if ((u.bi_flags & (BCH_INODE_i_size_dirty|BCH_INODE_unlinked)) &&
|
bcachefs: bch2_propagate_key_to_snapshot_leaves()
If fsck finds a key that needs work done, the primary example being an
unlinked inode that needs to be deleted, and the key is in an internal
snapshot node, we have a bit of a conundrum.
The conundrum is that internal snapshot nodes are shared, and we in
general do updates in internal snapshot nodes because there may be
overwrites in some snapshots and not others, and this may affect other
keys referenced by this key (i.e. extents).
For example, we might be seeing an unlinked inode in an internal
snapshot node, but then in one child snapshot the inode might have been
reattached and might not be unlinked. Deleting the inode in the internal
snapshot node would be wrong, because then we'll delete all the extents
that the child snapshot references.
But if an unlinked inode does not have any overwrites in child
snapshots, we're fine: the inode is overwrritten in all child snapshots,
so we can do the deletion at the point of comonality in the snapshot
tree, i.e. the node where we found it.
This patch adds a new helper, bch2_propagate_key_to_snapshot_leaves(),
to handle the case where we need a to update a key that does have
overwrites in child snapshots: we copy the key to leaf snapshot nodes,
and then rewind fsck and process the needed updates there.
With this, fsck can now always correctly handle unlinked inodes found in
internal snapshot nodes.
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
2023-08-19 01:14:33 +00:00
|
|
|
bch2_key_has_snapshot_overwrites(trans, BTREE_ID_inodes, k.k->p)) {
|
|
|
|
struct bpos new_min_pos;
|
|
|
|
|
|
|
|
ret = bch2_propagate_key_to_snapshot_leaves(trans, iter->btree_id, k, &new_min_pos);
|
|
|
|
if (ret)
|
|
|
|
goto err;
|
|
|
|
|
2023-11-02 15:42:48 +00:00
|
|
|
u.bi_flags &= ~BCH_INODE_i_size_dirty|BCH_INODE_unlinked;
|
bcachefs: bch2_propagate_key_to_snapshot_leaves()
If fsck finds a key that needs work done, the primary example being an
unlinked inode that needs to be deleted, and the key is in an internal
snapshot node, we have a bit of a conundrum.
The conundrum is that internal snapshot nodes are shared, and we in
general do updates in internal snapshot nodes because there may be
overwrites in some snapshots and not others, and this may affect other
keys referenced by this key (i.e. extents).
For example, we might be seeing an unlinked inode in an internal
snapshot node, but then in one child snapshot the inode might have been
reattached and might not be unlinked. Deleting the inode in the internal
snapshot node would be wrong, because then we'll delete all the extents
that the child snapshot references.
But if an unlinked inode does not have any overwrites in child
snapshots, we're fine: the inode is overwrritten in all child snapshots,
so we can do the deletion at the point of comonality in the snapshot
tree, i.e. the node where we found it.
This patch adds a new helper, bch2_propagate_key_to_snapshot_leaves(),
to handle the case where we need a to update a key that does have
overwrites in child snapshots: we copy the key to leaf snapshot nodes,
and then rewind fsck and process the needed updates there.
With this, fsck can now always correctly handle unlinked inodes found in
internal snapshot nodes.
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
2023-08-19 01:14:33 +00:00
|
|
|
|
|
|
|
ret = __write_inode(trans, &u, iter->pos.snapshot);
|
2023-09-26 20:02:06 +00:00
|
|
|
bch_err_msg(c, ret, "in fsck updating inode");
|
|
|
|
if (ret)
|
bcachefs: bch2_propagate_key_to_snapshot_leaves()
If fsck finds a key that needs work done, the primary example being an
unlinked inode that needs to be deleted, and the key is in an internal
snapshot node, we have a bit of a conundrum.
The conundrum is that internal snapshot nodes are shared, and we in
general do updates in internal snapshot nodes because there may be
overwrites in some snapshots and not others, and this may affect other
keys referenced by this key (i.e. extents).
For example, we might be seeing an unlinked inode in an internal
snapshot node, but then in one child snapshot the inode might have been
reattached and might not be unlinked. Deleting the inode in the internal
snapshot node would be wrong, because then we'll delete all the extents
that the child snapshot references.
But if an unlinked inode does not have any overwrites in child
snapshots, we're fine: the inode is overwrritten in all child snapshots,
so we can do the deletion at the point of comonality in the snapshot
tree, i.e. the node where we found it.
This patch adds a new helper, bch2_propagate_key_to_snapshot_leaves(),
to handle the case where we need a to update a key that does have
overwrites in child snapshots: we copy the key to leaf snapshot nodes,
and then rewind fsck and process the needed updates there.
With this, fsck can now always correctly handle unlinked inodes found in
internal snapshot nodes.
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
2023-08-19 01:14:33 +00:00
|
|
|
return ret;
|
|
|
|
|
|
|
|
if (!bpos_eq(new_min_pos, POS_MIN))
|
|
|
|
bch2_btree_iter_set_pos(iter, bpos_predecessor(new_min_pos));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2024-01-04 04:29:02 +00:00
|
|
|
if (u.bi_flags & BCH_INODE_unlinked) {
|
2023-11-16 22:24:54 +00:00
|
|
|
ret = check_inode_deleted_list(trans, k.k->p);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
fsck_err_on(ret, c, unlinked_inode_not_on_deleted_list,
|
|
|
|
"inode %llu:%u unlinked, but not on deleted list",
|
|
|
|
u.bi_inum, k.k->p.snapshot);
|
|
|
|
ret = 0;
|
|
|
|
}
|
|
|
|
|
2023-11-02 15:42:48 +00:00
|
|
|
if (u.bi_flags & BCH_INODE_unlinked &&
|
2021-04-07 01:41:48 +00:00
|
|
|
(!c->sb.clean ||
|
2023-10-25 00:44:36 +00:00
|
|
|
fsck_err(c, inode_unlinked_but_clean,
|
|
|
|
"filesystem marked clean, but inode %llu unlinked",
|
2021-04-07 01:41:48 +00:00
|
|
|
u.bi_inum))) {
|
2023-07-21 07:20:08 +00:00
|
|
|
ret = bch2_inode_rm_snapshot(trans, u.bi_inum, iter->pos.snapshot);
|
2023-09-26 20:02:06 +00:00
|
|
|
bch_err_msg(c, ret, "in fsck deleting inode");
|
2021-04-07 01:41:48 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2023-11-02 15:42:48 +00:00
|
|
|
if (u.bi_flags & BCH_INODE_i_size_dirty &&
|
2021-04-07 01:41:48 +00:00
|
|
|
(!c->sb.clean ||
|
2023-10-25 00:44:36 +00:00
|
|
|
fsck_err(c, inode_i_size_dirty_but_clean,
|
|
|
|
"filesystem marked clean, but inode %llu has i_size dirty",
|
2021-04-07 01:41:48 +00:00
|
|
|
u.bi_inum))) {
|
|
|
|
bch_verbose(c, "truncating inode %llu", u.bi_inum);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XXX: need to truncate partial blocks too here - or ideally
|
|
|
|
* just switch units to bytes and that issue goes away
|
|
|
|
*/
|
|
|
|
ret = bch2_btree_delete_range_trans(trans, BTREE_ID_extents,
|
2021-04-20 04:15:44 +00:00
|
|
|
SPOS(u.bi_inum, round_up(u.bi_size, block_bytes(c)) >> 9,
|
|
|
|
iter->pos.snapshot),
|
2021-04-07 01:41:48 +00:00
|
|
|
POS(u.bi_inum, U64_MAX),
|
2021-04-20 04:15:44 +00:00
|
|
|
0, NULL);
|
2023-09-26 20:02:06 +00:00
|
|
|
bch_err_msg(c, ret, "in fsck truncating inode");
|
2023-03-10 19:34:30 +00:00
|
|
|
if (ret)
|
2021-04-07 01:41:48 +00:00
|
|
|
return ret;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We truncated without our normal sector accounting hook, just
|
|
|
|
* make sure we recalculate it:
|
|
|
|
*/
|
2023-11-02 15:42:48 +00:00
|
|
|
u.bi_flags |= BCH_INODE_i_sectors_dirty;
|
2021-04-07 01:41:48 +00:00
|
|
|
|
2023-11-02 15:42:48 +00:00
|
|
|
u.bi_flags &= ~BCH_INODE_i_size_dirty;
|
2021-04-07 01:41:48 +00:00
|
|
|
do_update = true;
|
|
|
|
}
|
|
|
|
|
2023-11-02 15:42:48 +00:00
|
|
|
if (u.bi_flags & BCH_INODE_i_sectors_dirty &&
|
2021-04-07 01:41:48 +00:00
|
|
|
(!c->sb.clean ||
|
2023-10-25 00:44:36 +00:00
|
|
|
fsck_err(c, inode_i_sectors_dirty_but_clean,
|
|
|
|
"filesystem marked clean, but inode %llu has i_sectors dirty",
|
2021-04-07 01:41:48 +00:00
|
|
|
u.bi_inum))) {
|
|
|
|
s64 sectors;
|
|
|
|
|
|
|
|
bch_verbose(c, "recounting sectors for inode %llu",
|
|
|
|
u.bi_inum);
|
|
|
|
|
2021-04-20 04:15:44 +00:00
|
|
|
sectors = bch2_count_inode_sectors(trans, u.bi_inum, iter->pos.snapshot);
|
2021-04-07 01:41:48 +00:00
|
|
|
if (sectors < 0) {
|
2023-09-26 20:02:06 +00:00
|
|
|
bch_err_msg(c, sectors, "in fsck recounting inode sectors");
|
2021-04-07 01:41:48 +00:00
|
|
|
return sectors;
|
|
|
|
}
|
|
|
|
|
|
|
|
u.bi_sectors = sectors;
|
2023-11-02 15:42:48 +00:00
|
|
|
u.bi_flags &= ~BCH_INODE_i_sectors_dirty;
|
2021-04-07 01:41:48 +00:00
|
|
|
do_update = true;
|
|
|
|
}
|
|
|
|
|
2023-11-02 15:42:48 +00:00
|
|
|
if (u.bi_flags & BCH_INODE_backptr_untrusted) {
|
2021-04-07 07:11:07 +00:00
|
|
|
u.bi_dir = 0;
|
|
|
|
u.bi_dir_offset = 0;
|
2023-11-02 15:42:48 +00:00
|
|
|
u.bi_flags &= ~BCH_INODE_backptr_untrusted;
|
2021-04-07 01:41:48 +00:00
|
|
|
do_update = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (do_update) {
|
2022-07-16 00:51:09 +00:00
|
|
|
ret = __write_inode(trans, &u, iter->pos.snapshot);
|
2023-09-26 20:02:06 +00:00
|
|
|
bch_err_msg(c, ret, "in fsck updating inode");
|
2023-09-26 05:39:25 +00:00
|
|
|
if (ret)
|
bcachefs: bch2_propagate_key_to_snapshot_leaves()
If fsck finds a key that needs work done, the primary example being an
unlinked inode that needs to be deleted, and the key is in an internal
snapshot node, we have a bit of a conundrum.
The conundrum is that internal snapshot nodes are shared, and we in
general do updates in internal snapshot nodes because there may be
overwrites in some snapshots and not others, and this may affect other
keys referenced by this key (i.e. extents).
For example, we might be seeing an unlinked inode in an internal
snapshot node, but then in one child snapshot the inode might have been
reattached and might not be unlinked. Deleting the inode in the internal
snapshot node would be wrong, because then we'll delete all the extents
that the child snapshot references.
But if an unlinked inode does not have any overwrites in child
snapshots, we're fine: the inode is overwrritten in all child snapshots,
so we can do the deletion at the point of comonality in the snapshot
tree, i.e. the node where we found it.
This patch adds a new helper, bch2_propagate_key_to_snapshot_leaves(),
to handle the case where we need a to update a key that does have
overwrites in child snapshots: we copy the key to leaf snapshot nodes,
and then rewind fsck and process the needed updates there.
With this, fsck can now always correctly handle unlinked inodes found in
internal snapshot nodes.
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
2023-08-19 01:14:33 +00:00
|
|
|
return ret;
|
2021-04-07 01:41:48 +00:00
|
|
|
}
|
2022-04-12 02:36:53 +00:00
|
|
|
err:
|
2021-04-07 01:41:48 +00:00
|
|
|
fsck_err:
|
2023-09-26 20:02:06 +00:00
|
|
|
bch_err_fn(c, ret);
|
2021-04-07 01:41:48 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2023-07-07 06:42:28 +00:00
|
|
|
int bch2_check_inodes(struct bch_fs *c)
|
2021-04-07 01:41:48 +00:00
|
|
|
{
|
2023-07-07 06:42:28 +00:00
|
|
|
bool full = c->opts.fsck;
|
2021-10-28 20:16:55 +00:00
|
|
|
struct bch_inode_unpacked prev = { 0 };
|
2022-07-14 06:47:36 +00:00
|
|
|
struct snapshots_seen s;
|
2021-04-07 01:41:48 +00:00
|
|
|
|
2022-07-14 06:47:36 +00:00
|
|
|
snapshots_seen_init(&s);
|
2021-04-07 01:41:48 +00:00
|
|
|
|
2023-12-17 08:07:26 +00:00
|
|
|
int ret = bch2_trans_run(c,
|
|
|
|
for_each_btree_key_commit(trans, iter, BTREE_ID_inodes,
|
|
|
|
POS_MIN,
|
|
|
|
BTREE_ITER_PREFETCH|BTREE_ITER_ALL_SNAPSHOTS, k,
|
|
|
|
NULL, NULL, BCH_TRANS_COMMIT_no_enospc,
|
|
|
|
check_inode(trans, &iter, k, &prev, &s, full)));
|
2021-04-20 04:15:44 +00:00
|
|
|
|
2022-07-14 06:47:36 +00:00
|
|
|
snapshots_seen_exit(&s);
|
2023-09-26 20:02:06 +00:00
|
|
|
bch_err_fn(c, ret);
|
2021-10-28 20:16:55 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2021-04-20 04:15:44 +00:00
|
|
|
|
2021-10-12 16:06:02 +00:00
|
|
|
static struct bkey_s_c_dirent dirent_get_by_pos(struct btree_trans *trans,
|
|
|
|
struct btree_iter *iter,
|
|
|
|
struct bpos pos)
|
|
|
|
{
|
2023-04-29 23:33:09 +00:00
|
|
|
return bch2_bkey_get_iter_typed(trans, iter, BTREE_ID_dirents, pos, 0, dirent);
|
2021-10-12 16:06:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool inode_points_to_dirent(struct bch_inode_unpacked *inode,
|
|
|
|
struct bkey_s_c_dirent d)
|
|
|
|
{
|
|
|
|
return inode->bi_dir == d.k->p.inode &&
|
|
|
|
inode->bi_dir_offset == d.k->p.offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool dirent_points_to_inode(struct bkey_s_c_dirent d,
|
|
|
|
struct bch_inode_unpacked *inode)
|
|
|
|
{
|
|
|
|
return d.v->d_type == DT_SUBVOL
|
|
|
|
? le32_to_cpu(d.v->d_child_subvol) == inode->bi_subvol
|
|
|
|
: le64_to_cpu(d.v->d_inum) == inode->bi_inum;
|
|
|
|
}
|
|
|
|
|
2021-04-20 04:15:44 +00:00
|
|
|
static int check_i_sectors(struct btree_trans *trans, struct inode_walker *w)
|
|
|
|
{
|
|
|
|
struct bch_fs *c = trans->c;
|
2022-07-22 10:57:05 +00:00
|
|
|
u32 restart_count = trans->restart_count;
|
|
|
|
int ret = 0;
|
2021-04-20 04:15:44 +00:00
|
|
|
s64 count2;
|
|
|
|
|
2022-03-29 19:48:45 +00:00
|
|
|
darray_for_each(w->inodes, i) {
|
2021-04-20 04:15:44 +00:00
|
|
|
if (i->inode.bi_sectors == i->count)
|
|
|
|
continue;
|
|
|
|
|
2023-07-16 18:24:36 +00:00
|
|
|
count2 = bch2_count_inode_sectors(trans, w->last_pos.inode, i->snapshot);
|
2021-04-20 04:15:44 +00:00
|
|
|
|
2023-07-13 07:11:16 +00:00
|
|
|
if (w->recalculate_sums)
|
2021-04-20 04:15:44 +00:00
|
|
|
i->count = count2;
|
2023-07-13 07:11:16 +00:00
|
|
|
|
|
|
|
if (i->count != count2) {
|
|
|
|
bch_err(c, "fsck counted i_sectors wrong for inode %llu:%u: got %llu should be %llu",
|
|
|
|
w->last_pos.inode, i->snapshot, i->count, count2);
|
|
|
|
return -BCH_ERR_internal_fsck_err;
|
2021-04-20 04:15:44 +00:00
|
|
|
}
|
|
|
|
|
2023-11-02 15:42:48 +00:00
|
|
|
if (fsck_err_on(!(i->inode.bi_flags & BCH_INODE_i_sectors_dirty),
|
2023-10-25 00:44:36 +00:00
|
|
|
c, inode_i_sectors_wrong,
|
|
|
|
"inode %llu:%u has incorrect i_sectors: got %llu, should be %llu",
|
|
|
|
w->last_pos.inode, i->snapshot,
|
|
|
|
i->inode.bi_sectors, i->count)) {
|
2022-07-19 21:20:18 +00:00
|
|
|
i->inode.bi_sectors = i->count;
|
2023-09-11 05:37:34 +00:00
|
|
|
ret = fsck_write_inode(trans, &i->inode, i->snapshot);
|
2022-07-19 21:20:18 +00:00
|
|
|
if (ret)
|
|
|
|
break;
|
|
|
|
}
|
2021-04-20 04:15:44 +00:00
|
|
|
}
|
|
|
|
fsck_err:
|
2023-09-26 20:02:06 +00:00
|
|
|
bch_err_fn(c, ret);
|
2023-09-10 20:24:02 +00:00
|
|
|
return ret ?: trans_was_restarted(trans, restart_count);
|
2021-04-20 04:15:44 +00:00
|
|
|
}
|
|
|
|
|
2022-05-08 03:03:28 +00:00
|
|
|
struct extent_end {
|
|
|
|
u32 snapshot;
|
|
|
|
u64 offset;
|
|
|
|
struct snapshots_seen seen;
|
|
|
|
};
|
|
|
|
|
2023-07-16 19:12:25 +00:00
|
|
|
struct extent_ends {
|
|
|
|
struct bpos last_pos;
|
|
|
|
DARRAY(struct extent_end) e;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void extent_ends_reset(struct extent_ends *extent_ends)
|
|
|
|
{
|
|
|
|
darray_for_each(extent_ends->e, i)
|
|
|
|
snapshots_seen_exit(&i->seen);
|
|
|
|
extent_ends->e.nr = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void extent_ends_exit(struct extent_ends *extent_ends)
|
|
|
|
{
|
|
|
|
extent_ends_reset(extent_ends);
|
|
|
|
darray_exit(&extent_ends->e);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void extent_ends_init(struct extent_ends *extent_ends)
|
|
|
|
{
|
|
|
|
memset(extent_ends, 0, sizeof(*extent_ends));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int extent_ends_at(struct bch_fs *c,
|
|
|
|
struct extent_ends *extent_ends,
|
|
|
|
struct snapshots_seen *seen,
|
|
|
|
struct bkey_s_c k)
|
|
|
|
{
|
|
|
|
struct extent_end *i, n = (struct extent_end) {
|
|
|
|
.offset = k.k->p.offset,
|
|
|
|
.snapshot = k.k->p.snapshot,
|
|
|
|
.seen = *seen,
|
|
|
|
};
|
|
|
|
|
|
|
|
n.seen.ids.data = kmemdup(seen->ids.data,
|
|
|
|
sizeof(seen->ids.data[0]) * seen->ids.size,
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!n.seen.ids.data)
|
|
|
|
return -BCH_ERR_ENOMEM_fsck_extent_ends_at;
|
|
|
|
|
2023-12-17 02:40:26 +00:00
|
|
|
__darray_for_each(extent_ends->e, i) {
|
2023-07-16 19:12:25 +00:00
|
|
|
if (i->snapshot == k.k->p.snapshot) {
|
|
|
|
snapshots_seen_exit(&i->seen);
|
|
|
|
*i = n;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i->snapshot >= k.k->p.snapshot)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return darray_insert_item(&extent_ends->e, i - extent_ends->e.data, n);
|
|
|
|
}
|
2022-05-08 03:03:28 +00:00
|
|
|
|
2023-07-13 07:11:16 +00:00
|
|
|
static int overlapping_extents_found(struct btree_trans *trans,
|
|
|
|
enum btree_id btree,
|
2023-07-21 02:42:26 +00:00
|
|
|
struct bpos pos1, struct snapshots_seen *pos1_seen,
|
|
|
|
struct bkey pos2,
|
|
|
|
bool *fixed,
|
|
|
|
struct extent_end *extent_end)
|
2023-06-25 03:22:20 +00:00
|
|
|
{
|
2023-07-13 07:11:16 +00:00
|
|
|
struct bch_fs *c = trans->c;
|
|
|
|
struct printbuf buf = PRINTBUF;
|
2023-07-21 02:42:26 +00:00
|
|
|
struct btree_iter iter1, iter2 = { NULL };
|
|
|
|
struct bkey_s_c k1, k2;
|
2023-06-25 03:22:20 +00:00
|
|
|
int ret;
|
|
|
|
|
2023-07-13 07:11:16 +00:00
|
|
|
BUG_ON(bkey_le(pos1, bkey_start_pos(&pos2)));
|
|
|
|
|
2023-07-21 02:42:26 +00:00
|
|
|
bch2_trans_iter_init(trans, &iter1, btree, pos1,
|
|
|
|
BTREE_ITER_ALL_SNAPSHOTS|
|
|
|
|
BTREE_ITER_NOT_EXTENTS);
|
|
|
|
k1 = bch2_btree_iter_peek_upto(&iter1, POS(pos1.inode, U64_MAX));
|
|
|
|
ret = bkey_err(k1);
|
2023-06-25 03:22:20 +00:00
|
|
|
if (ret)
|
2023-07-13 07:11:16 +00:00
|
|
|
goto err;
|
|
|
|
|
|
|
|
prt_str(&buf, "\n ");
|
2023-07-21 02:42:26 +00:00
|
|
|
bch2_bkey_val_to_text(&buf, c, k1);
|
2023-07-13 07:11:16 +00:00
|
|
|
|
2023-07-21 02:42:26 +00:00
|
|
|
if (!bpos_eq(pos1, k1.k->p)) {
|
|
|
|
prt_str(&buf, "\n wanted\n ");
|
|
|
|
bch2_bpos_to_text(&buf, pos1);
|
|
|
|
prt_str(&buf, "\n ");
|
|
|
|
bch2_bkey_to_text(&buf, &pos2);
|
|
|
|
|
|
|
|
bch_err(c, "%s: error finding first overlapping extent when repairing, got%s",
|
2023-07-13 07:11:16 +00:00
|
|
|
__func__, buf.buf);
|
|
|
|
ret = -BCH_ERR_internal_fsck_err;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2023-07-21 02:42:26 +00:00
|
|
|
bch2_trans_copy_iter(&iter2, &iter1);
|
|
|
|
|
2023-07-13 07:11:16 +00:00
|
|
|
while (1) {
|
2023-07-21 02:42:26 +00:00
|
|
|
bch2_btree_iter_advance(&iter2);
|
2023-07-13 07:11:16 +00:00
|
|
|
|
2023-07-21 02:42:26 +00:00
|
|
|
k2 = bch2_btree_iter_peek_upto(&iter2, POS(pos1.inode, U64_MAX));
|
|
|
|
ret = bkey_err(k2);
|
2023-07-13 07:11:16 +00:00
|
|
|
if (ret)
|
|
|
|
goto err;
|
|
|
|
|
2023-07-21 02:42:26 +00:00
|
|
|
if (bpos_ge(k2.k->p, pos2.p))
|
2023-07-13 07:11:16 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
prt_str(&buf, "\n ");
|
2023-07-21 02:42:26 +00:00
|
|
|
bch2_bkey_val_to_text(&buf, c, k2);
|
2023-07-13 07:11:16 +00:00
|
|
|
|
2023-07-21 02:42:26 +00:00
|
|
|
if (bpos_gt(k2.k->p, pos2.p) ||
|
|
|
|
pos2.size != k2.k->size) {
|
2023-07-13 07:11:16 +00:00
|
|
|
bch_err(c, "%s: error finding seconding overlapping extent when repairing%s",
|
|
|
|
__func__, buf.buf);
|
|
|
|
ret = -BCH_ERR_internal_fsck_err;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2023-07-21 02:42:26 +00:00
|
|
|
prt_printf(&buf, "\n overwriting %s extent",
|
|
|
|
pos1.snapshot >= pos2.p.snapshot ? "first" : "second");
|
|
|
|
|
2023-10-25 00:44:36 +00:00
|
|
|
if (fsck_err(c, extent_overlapping,
|
|
|
|
"overlapping extents%s", buf.buf)) {
|
2023-07-21 02:42:26 +00:00
|
|
|
struct btree_iter *old_iter = &iter1;
|
|
|
|
struct disk_reservation res = { 0 };
|
2023-06-25 03:22:20 +00:00
|
|
|
|
2023-07-21 02:42:26 +00:00
|
|
|
if (pos1.snapshot < pos2.p.snapshot) {
|
|
|
|
old_iter = &iter2;
|
|
|
|
swap(k1, k2);
|
|
|
|
}
|
|
|
|
|
2023-12-11 07:31:12 +00:00
|
|
|
trans->extra_disk_res += bch2_bkey_sectors_compressed(k2);
|
2023-07-21 02:42:26 +00:00
|
|
|
|
|
|
|
ret = bch2_trans_update_extent_overwrite(trans, old_iter,
|
|
|
|
BTREE_UPDATE_INTERNAL_SNAPSHOT_NODE,
|
|
|
|
k1, k2) ?:
|
2023-11-28 21:36:54 +00:00
|
|
|
bch2_trans_commit(trans, &res, NULL, BCH_TRANS_COMMIT_no_enospc);
|
2023-07-21 02:42:26 +00:00
|
|
|
bch2_disk_reservation_put(c, &res);
|
|
|
|
|
|
|
|
if (ret)
|
2023-07-13 07:11:16 +00:00
|
|
|
goto err;
|
|
|
|
|
|
|
|
*fixed = true;
|
2023-07-21 02:42:26 +00:00
|
|
|
|
|
|
|
if (pos1.snapshot == pos2.p.snapshot) {
|
|
|
|
/*
|
|
|
|
* We overwrote the first extent, and did the overwrite
|
|
|
|
* in the same snapshot:
|
|
|
|
*/
|
|
|
|
extent_end->offset = bkey_start_offset(&pos2);
|
|
|
|
} else if (pos1.snapshot > pos2.p.snapshot) {
|
|
|
|
/*
|
|
|
|
* We overwrote the first extent in pos2's snapshot:
|
|
|
|
*/
|
|
|
|
ret = snapshots_seen_add_inorder(c, pos1_seen, pos2.p.snapshot);
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* We overwrote the second extent - restart
|
|
|
|
* check_extent() from the top:
|
|
|
|
*/
|
|
|
|
ret = -BCH_ERR_transaction_restart_nested;
|
|
|
|
}
|
2023-07-13 07:11:16 +00:00
|
|
|
}
|
|
|
|
fsck_err:
|
|
|
|
err:
|
2023-07-21 02:42:26 +00:00
|
|
|
bch2_trans_iter_exit(trans, &iter2);
|
|
|
|
bch2_trans_iter_exit(trans, &iter1);
|
2023-07-13 07:11:16 +00:00
|
|
|
printbuf_exit(&buf);
|
|
|
|
return ret;
|
2023-06-25 03:22:20 +00:00
|
|
|
}
|
|
|
|
|
2022-05-08 03:03:28 +00:00
|
|
|
static int check_overlapping_extents(struct btree_trans *trans,
|
|
|
|
struct snapshots_seen *seen,
|
2023-07-16 19:12:25 +00:00
|
|
|
struct extent_ends *extent_ends,
|
2022-05-08 03:03:28 +00:00
|
|
|
struct bkey_s_c k,
|
2023-07-16 19:12:25 +00:00
|
|
|
u32 equiv,
|
2023-07-21 02:42:26 +00:00
|
|
|
struct btree_iter *iter,
|
|
|
|
bool *fixed)
|
2022-05-08 03:03:28 +00:00
|
|
|
{
|
|
|
|
struct bch_fs *c = trans->c;
|
|
|
|
int ret = 0;
|
|
|
|
|
2023-07-16 19:12:25 +00:00
|
|
|
/* transaction restart, running again */
|
|
|
|
if (bpos_eq(extent_ends->last_pos, k.k->p))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (extent_ends->last_pos.inode != k.k->p.inode)
|
|
|
|
extent_ends_reset(extent_ends);
|
|
|
|
|
|
|
|
darray_for_each(extent_ends->e, i) {
|
|
|
|
if (i->offset <= bkey_start_offset(k.k))
|
2022-05-08 03:03:28 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!ref_visible2(c,
|
|
|
|
k.k->p.snapshot, seen,
|
|
|
|
i->snapshot, &i->seen))
|
|
|
|
continue;
|
|
|
|
|
2023-07-16 19:12:25 +00:00
|
|
|
ret = overlapping_extents_found(trans, iter->btree_id,
|
|
|
|
SPOS(iter->pos.inode,
|
|
|
|
i->offset,
|
|
|
|
i->snapshot),
|
2023-07-21 02:42:26 +00:00
|
|
|
&i->seen,
|
|
|
|
*k.k, fixed, i);
|
2023-07-16 19:12:25 +00:00
|
|
|
if (ret)
|
|
|
|
goto err;
|
2022-05-08 03:03:28 +00:00
|
|
|
}
|
|
|
|
|
2023-07-16 19:12:25 +00:00
|
|
|
ret = extent_ends_at(c, extent_ends, seen, k);
|
|
|
|
if (ret)
|
|
|
|
goto err;
|
2022-05-08 03:03:28 +00:00
|
|
|
|
2023-07-16 19:12:25 +00:00
|
|
|
extent_ends->last_pos = k.k->p;
|
|
|
|
err:
|
2023-07-21 02:42:26 +00:00
|
|
|
return ret;
|
2022-05-08 03:03:28 +00:00
|
|
|
}
|
|
|
|
|
2023-10-22 15:33:02 +00:00
|
|
|
static int check_extent_overbig(struct btree_trans *trans, struct btree_iter *iter,
|
|
|
|
struct bkey_s_c k)
|
|
|
|
{
|
|
|
|
struct bch_fs *c = trans->c;
|
|
|
|
struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k);
|
|
|
|
struct bch_extent_crc_unpacked crc;
|
|
|
|
const union bch_extent_entry *i;
|
|
|
|
unsigned encoded_extent_max_sectors = c->opts.encoded_extent_max >> 9;
|
|
|
|
|
|
|
|
bkey_for_each_crc(k.k, ptrs, crc, i)
|
|
|
|
if (crc_is_encoded(crc) &&
|
|
|
|
crc.uncompressed_size > encoded_extent_max_sectors) {
|
|
|
|
struct printbuf buf = PRINTBUF;
|
|
|
|
|
|
|
|
bch2_bkey_val_to_text(&buf, c, k);
|
|
|
|
bch_err(c, "overbig encoded extent, please report this:\n %s", buf.buf);
|
|
|
|
printbuf_exit(&buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-04-20 04:15:44 +00:00
|
|
|
static int check_extent(struct btree_trans *trans, struct btree_iter *iter,
|
2022-07-16 00:51:09 +00:00
|
|
|
struct bkey_s_c k,
|
2021-04-20 04:15:44 +00:00
|
|
|
struct inode_walker *inode,
|
2022-05-08 03:03:28 +00:00
|
|
|
struct snapshots_seen *s,
|
2023-07-16 19:12:25 +00:00
|
|
|
struct extent_ends *extent_ends)
|
2021-04-20 04:15:44 +00:00
|
|
|
{
|
|
|
|
struct bch_fs *c = trans->c;
|
|
|
|
struct inode_walker_entry *i;
|
2022-02-25 18:18:19 +00:00
|
|
|
struct printbuf buf = PRINTBUF;
|
2023-07-13 05:41:02 +00:00
|
|
|
struct bpos equiv = k.k->p;
|
2021-04-20 04:15:44 +00:00
|
|
|
int ret = 0;
|
|
|
|
|
2023-07-13 05:41:02 +00:00
|
|
|
equiv.snapshot = bch2_snapshot_equiv(c, k.k->p.snapshot);
|
|
|
|
|
2021-04-20 04:15:44 +00:00
|
|
|
ret = check_key_has_snapshot(trans, iter, k);
|
2022-02-25 18:18:19 +00:00
|
|
|
if (ret) {
|
|
|
|
ret = ret < 0 ? ret : 0;
|
|
|
|
goto out;
|
|
|
|
}
|
2021-04-20 04:15:44 +00:00
|
|
|
|
2023-07-16 18:24:36 +00:00
|
|
|
if (inode->last_pos.inode != k.k->p.inode) {
|
2021-04-20 04:15:44 +00:00
|
|
|
ret = check_i_sectors(trans, inode);
|
|
|
|
if (ret)
|
2022-02-25 18:18:19 +00:00
|
|
|
goto err;
|
2021-04-20 04:15:44 +00:00
|
|
|
}
|
2022-04-06 18:35:10 +00:00
|
|
|
|
2023-07-16 18:19:08 +00:00
|
|
|
i = walk_inode(trans, inode, equiv, k.k->type == KEY_TYPE_whiteout);
|
|
|
|
ret = PTR_ERR_OR_ZERO(i);
|
|
|
|
if (ret)
|
|
|
|
goto err;
|
|
|
|
|
2023-07-16 18:45:23 +00:00
|
|
|
ret = snapshots_seen_update(c, s, iter->btree_id, k.k->p);
|
2022-05-08 03:03:28 +00:00
|
|
|
if (ret)
|
|
|
|
goto err;
|
2021-04-20 04:15:44 +00:00
|
|
|
|
2023-07-16 18:45:23 +00:00
|
|
|
if (k.k->type != KEY_TYPE_whiteout) {
|
2023-10-25 00:44:36 +00:00
|
|
|
if (fsck_err_on(!i, c, extent_in_missing_inode,
|
2023-07-16 18:45:23 +00:00
|
|
|
"extent in missing inode:\n %s",
|
|
|
|
(printbuf_reset(&buf),
|
|
|
|
bch2_bkey_val_to_text(&buf, c, k), buf.buf)))
|
|
|
|
goto delete;
|
|
|
|
|
|
|
|
if (fsck_err_on(i &&
|
|
|
|
!S_ISREG(i->inode.bi_mode) &&
|
2023-10-25 00:44:36 +00:00
|
|
|
!S_ISLNK(i->inode.bi_mode),
|
|
|
|
c, extent_in_non_reg_inode,
|
2023-07-16 18:45:23 +00:00
|
|
|
"extent in non regular inode mode %o:\n %s",
|
|
|
|
i->inode.bi_mode,
|
|
|
|
(printbuf_reset(&buf),
|
|
|
|
bch2_bkey_val_to_text(&buf, c, k), buf.buf)))
|
|
|
|
goto delete;
|
2021-04-20 04:15:44 +00:00
|
|
|
|
2023-07-16 19:12:25 +00:00
|
|
|
ret = check_overlapping_extents(trans, s, extent_ends, k,
|
2023-07-21 02:42:26 +00:00
|
|
|
equiv.snapshot, iter,
|
|
|
|
&inode->recalculate_sums);
|
2023-07-16 18:45:23 +00:00
|
|
|
if (ret)
|
2023-07-21 02:42:26 +00:00
|
|
|
goto err;
|
2023-07-16 18:45:23 +00:00
|
|
|
}
|
2021-04-20 04:15:44 +00:00
|
|
|
|
2022-07-17 03:31:28 +00:00
|
|
|
/*
|
2023-07-16 18:55:33 +00:00
|
|
|
* Check inodes in reverse order, from oldest snapshots to newest,
|
|
|
|
* starting from the inode that matches this extent's snapshot. If we
|
|
|
|
* didn't have one, iterate over all inodes:
|
2022-07-17 03:31:28 +00:00
|
|
|
*/
|
2023-07-16 18:55:33 +00:00
|
|
|
if (!i)
|
|
|
|
i = inode->inodes.data + inode->inodes.nr - 1;
|
|
|
|
|
|
|
|
for (;
|
2023-07-16 18:45:23 +00:00
|
|
|
inode->inodes.data && i >= inode->inodes.data;
|
2022-07-17 03:31:28 +00:00
|
|
|
--i) {
|
|
|
|
if (i->snapshot > equiv.snapshot ||
|
|
|
|
!key_visible_in_snapshot(c, s, i->snapshot, equiv.snapshot))
|
|
|
|
continue;
|
|
|
|
|
2023-07-16 18:45:23 +00:00
|
|
|
if (k.k->type != KEY_TYPE_whiteout) {
|
2023-11-02 15:42:48 +00:00
|
|
|
if (fsck_err_on(!(i->inode.bi_flags & BCH_INODE_i_size_dirty) &&
|
2023-07-16 18:45:23 +00:00
|
|
|
k.k->p.offset > round_up(i->inode.bi_size, block_bytes(c)) >> 9 &&
|
2023-10-25 00:44:36 +00:00
|
|
|
!bkey_extent_is_reservation(k),
|
|
|
|
c, extent_past_end_of_inode,
|
2023-07-16 18:45:23 +00:00
|
|
|
"extent type past end of inode %llu:%u, i_size %llu\n %s",
|
|
|
|
i->inode.bi_inum, i->snapshot, i->inode.bi_size,
|
|
|
|
(bch2_bkey_val_to_text(&buf, c, k), buf.buf))) {
|
|
|
|
struct btree_iter iter2;
|
|
|
|
|
|
|
|
bch2_trans_copy_iter(&iter2, iter);
|
|
|
|
bch2_btree_iter_set_snapshot(&iter2, i->snapshot);
|
|
|
|
ret = bch2_btree_iter_traverse(&iter2) ?:
|
|
|
|
bch2_btree_delete_at(trans, &iter2,
|
|
|
|
BTREE_UPDATE_INTERNAL_SNAPSHOT_NODE);
|
|
|
|
bch2_trans_iter_exit(trans, &iter2);
|
2022-07-17 03:31:28 +00:00
|
|
|
if (ret)
|
|
|
|
goto err;
|
2023-07-16 18:45:23 +00:00
|
|
|
|
|
|
|
iter->k.type = KEY_TYPE_whiteout;
|
2021-04-20 04:15:44 +00:00
|
|
|
}
|
2023-07-16 18:55:33 +00:00
|
|
|
|
|
|
|
if (bkey_extent_is_allocation(k.k))
|
|
|
|
i->count += k.k->size;
|
2021-04-20 04:15:44 +00:00
|
|
|
}
|
2021-04-07 00:15:26 +00:00
|
|
|
|
2023-07-16 18:55:33 +00:00
|
|
|
i->seen_this_pos = true;
|
|
|
|
}
|
2022-02-25 18:18:19 +00:00
|
|
|
out:
|
|
|
|
err:
|
2021-04-20 04:15:44 +00:00
|
|
|
fsck_err:
|
2022-02-25 18:18:19 +00:00
|
|
|
printbuf_exit(&buf);
|
2023-09-26 20:02:06 +00:00
|
|
|
bch_err_fn(c, ret);
|
2021-04-07 00:15:26 +00:00
|
|
|
return ret;
|
2023-07-13 05:41:02 +00:00
|
|
|
delete:
|
|
|
|
ret = bch2_btree_delete_at(trans, iter, BTREE_UPDATE_INTERNAL_SNAPSHOT_NODE);
|
|
|
|
goto out;
|
2021-04-07 00:15:26 +00:00
|
|
|
}
|
|
|
|
|
2017-03-17 06:18:50 +00:00
|
|
|
/*
|
|
|
|
* Walk extents: verify that extents have a corresponding S_ISREG inode, and
|
|
|
|
* that i_size an i_sectors are consistent
|
|
|
|
*/
|
2023-07-07 06:42:28 +00:00
|
|
|
int bch2_check_extents(struct bch_fs *c)
|
2017-03-17 06:18:50 +00:00
|
|
|
{
|
|
|
|
struct inode_walker w = inode_walker_init();
|
2021-04-20 04:15:44 +00:00
|
|
|
struct snapshots_seen s;
|
2023-07-16 19:12:25 +00:00
|
|
|
struct extent_ends extent_ends;
|
2023-04-17 01:49:12 +00:00
|
|
|
struct disk_reservation res = { 0 };
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2021-04-20 04:15:44 +00:00
|
|
|
snapshots_seen_init(&s);
|
2023-07-16 19:12:25 +00:00
|
|
|
extent_ends_init(&extent_ends);
|
2019-03-25 19:10:15 +00:00
|
|
|
|
2023-12-17 08:07:26 +00:00
|
|
|
int ret = bch2_trans_run(c,
|
|
|
|
for_each_btree_key_commit(trans, iter, BTREE_ID_extents,
|
|
|
|
POS(BCACHEFS_ROOT_INO, 0),
|
|
|
|
BTREE_ITER_PREFETCH|BTREE_ITER_ALL_SNAPSHOTS, k,
|
|
|
|
&res, NULL,
|
|
|
|
BCH_TRANS_COMMIT_no_enospc, ({
|
|
|
|
bch2_disk_reservation_put(c, &res);
|
|
|
|
check_extent(trans, &iter, k, &w, &s, &extent_ends) ?:
|
|
|
|
check_extent_overbig(trans, &iter, k);
|
|
|
|
})) ?:
|
|
|
|
check_i_sectors(trans, &w));
|
2022-05-08 03:03:28 +00:00
|
|
|
|
2023-04-17 01:49:12 +00:00
|
|
|
bch2_disk_reservation_put(c, &res);
|
2023-07-16 19:12:25 +00:00
|
|
|
extent_ends_exit(&extent_ends);
|
2021-04-20 04:15:44 +00:00
|
|
|
inode_walker_exit(&w);
|
|
|
|
snapshots_seen_exit(&s);
|
|
|
|
|
2023-09-26 20:02:06 +00:00
|
|
|
bch_err_fn(c, ret);
|
2021-04-20 04:15:44 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2023-10-22 15:33:02 +00:00
|
|
|
int bch2_check_indirect_extents(struct bch_fs *c)
|
|
|
|
{
|
|
|
|
struct disk_reservation res = { 0 };
|
|
|
|
|
2023-12-17 08:07:26 +00:00
|
|
|
int ret = bch2_trans_run(c,
|
|
|
|
for_each_btree_key_commit(trans, iter, BTREE_ID_reflink,
|
|
|
|
POS_MIN,
|
|
|
|
BTREE_ITER_PREFETCH, k,
|
|
|
|
&res, NULL,
|
|
|
|
BCH_TRANS_COMMIT_no_enospc, ({
|
|
|
|
bch2_disk_reservation_put(c, &res);
|
|
|
|
check_extent_overbig(trans, &iter, k);
|
|
|
|
})));
|
2023-10-22 15:33:02 +00:00
|
|
|
|
|
|
|
bch2_disk_reservation_put(c, &res);
|
|
|
|
bch_err_fn(c, ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2021-04-20 04:15:44 +00:00
|
|
|
static int check_subdir_count(struct btree_trans *trans, struct inode_walker *w)
|
|
|
|
{
|
|
|
|
struct bch_fs *c = trans->c;
|
2022-07-22 10:57:05 +00:00
|
|
|
u32 restart_count = trans->restart_count;
|
|
|
|
int ret = 0;
|
2021-04-20 04:15:44 +00:00
|
|
|
s64 count2;
|
|
|
|
|
2022-03-29 19:48:45 +00:00
|
|
|
darray_for_each(w->inodes, i) {
|
2021-04-20 04:15:44 +00:00
|
|
|
if (i->inode.bi_nlink == i->count)
|
|
|
|
continue;
|
|
|
|
|
2023-07-16 18:24:36 +00:00
|
|
|
count2 = bch2_count_subdirs(trans, w->last_pos.inode, i->snapshot);
|
2022-02-14 01:42:12 +00:00
|
|
|
if (count2 < 0)
|
|
|
|
return count2;
|
2021-04-20 04:15:44 +00:00
|
|
|
|
|
|
|
if (i->count != count2) {
|
|
|
|
bch_err(c, "fsck counted subdirectories wrong: got %llu should be %llu",
|
|
|
|
i->count, count2);
|
|
|
|
i->count = count2;
|
|
|
|
if (i->inode.bi_nlink == i->count)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2023-10-25 00:44:36 +00:00
|
|
|
if (fsck_err_on(i->inode.bi_nlink != i->count,
|
|
|
|
c, inode_dir_wrong_nlink,
|
2021-04-20 04:15:44 +00:00
|
|
|
"directory %llu:%u with wrong i_nlink: got %u, should be %llu",
|
2023-07-16 18:24:36 +00:00
|
|
|
w->last_pos.inode, i->snapshot, i->inode.bi_nlink, i->count)) {
|
2021-04-20 04:15:44 +00:00
|
|
|
i->inode.bi_nlink = i->count;
|
2023-09-11 05:37:34 +00:00
|
|
|
ret = fsck_write_inode(trans, &i->inode, i->snapshot);
|
2021-03-20 02:34:54 +00:00
|
|
|
if (ret)
|
|
|
|
break;
|
|
|
|
}
|
2021-04-20 04:15:44 +00:00
|
|
|
}
|
|
|
|
fsck_err:
|
2023-09-26 20:02:06 +00:00
|
|
|
bch_err_fn(c, ret);
|
2023-09-10 20:24:02 +00:00
|
|
|
return ret ?: trans_was_restarted(trans, restart_count);
|
2021-04-20 04:15:44 +00:00
|
|
|
}
|
2021-03-20 02:34:54 +00:00
|
|
|
|
2021-04-20 04:15:44 +00:00
|
|
|
static int check_dirent_target(struct btree_trans *trans,
|
|
|
|
struct btree_iter *iter,
|
|
|
|
struct bkey_s_c_dirent d,
|
|
|
|
struct bch_inode_unpacked *target,
|
|
|
|
u32 target_snapshot)
|
|
|
|
{
|
|
|
|
struct bch_fs *c = trans->c;
|
2021-10-28 20:16:55 +00:00
|
|
|
struct bkey_i_dirent *n;
|
2022-02-25 18:18:19 +00:00
|
|
|
struct printbuf buf = PRINTBUF;
|
2023-11-14 02:24:24 +00:00
|
|
|
struct btree_iter bp_iter = { NULL };
|
2021-04-20 04:15:44 +00:00
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (!target->bi_dir &&
|
|
|
|
!target->bi_dir_offset) {
|
|
|
|
target->bi_dir = d.k->p.inode;
|
|
|
|
target->bi_dir_offset = d.k->p.offset;
|
|
|
|
|
2021-10-28 20:16:55 +00:00
|
|
|
ret = __write_inode(trans, target, target_snapshot);
|
2021-04-20 04:15:44 +00:00
|
|
|
if (ret)
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2021-10-12 16:06:02 +00:00
|
|
|
if (!inode_points_to_dirent(target, d)) {
|
2023-11-14 02:24:24 +00:00
|
|
|
struct bkey_s_c_dirent bp_dirent = dirent_get_by_pos(trans, &bp_iter,
|
|
|
|
SPOS(target->bi_dir, target->bi_dir_offset, target_snapshot));
|
|
|
|
ret = bkey_err(bp_dirent);
|
|
|
|
if (ret && !bch2_err_matches(ret, ENOENT))
|
2021-04-20 04:15:44 +00:00
|
|
|
goto err;
|
2019-12-30 19:37:25 +00:00
|
|
|
|
2023-11-14 02:24:24 +00:00
|
|
|
bool backpointer_exists = !ret;
|
2021-04-20 04:15:44 +00:00
|
|
|
ret = 0;
|
2019-12-30 19:37:25 +00:00
|
|
|
|
2023-11-14 02:24:24 +00:00
|
|
|
bch2_bkey_val_to_text(&buf, c, d.s_c);
|
|
|
|
prt_newline(&buf);
|
|
|
|
if (backpointer_exists)
|
|
|
|
bch2_bkey_val_to_text(&buf, c, bp_dirent.s_c);
|
|
|
|
|
2023-10-25 00:44:36 +00:00
|
|
|
if (fsck_err_on(S_ISDIR(target->bi_mode) && backpointer_exists,
|
|
|
|
c, inode_dir_multiple_links,
|
2023-11-14 02:24:24 +00:00
|
|
|
"directory %llu:%u with multiple links\n%s",
|
|
|
|
target->bi_inum, target_snapshot, buf.buf)) {
|
2021-10-28 20:16:55 +00:00
|
|
|
ret = __remove_dirent(trans, d.k->p);
|
2022-02-25 18:18:19 +00:00
|
|
|
goto out;
|
2019-12-30 19:37:25 +00:00
|
|
|
}
|
|
|
|
|
2023-11-14 02:24:24 +00:00
|
|
|
/*
|
|
|
|
* hardlinked file with nlink 0:
|
|
|
|
* We're just adjusting nlink here so check_nlinks() will pick
|
|
|
|
* it up, it ignores inodes with nlink 0
|
|
|
|
*/
|
2023-10-25 00:44:36 +00:00
|
|
|
if (fsck_err_on(backpointer_exists && !target->bi_nlink,
|
|
|
|
c, inode_multiple_links_but_nlink_0,
|
2023-11-14 02:24:24 +00:00
|
|
|
"inode %llu:%u type %s has multiple links but i_nlink 0\n%s",
|
|
|
|
target->bi_inum, target_snapshot, bch2_d_types[d.v->d_type], buf.buf)) {
|
2021-04-20 04:15:44 +00:00
|
|
|
target->bi_nlink++;
|
2023-11-02 15:42:48 +00:00
|
|
|
target->bi_flags &= ~BCH_INODE_unlinked;
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2021-10-28 20:16:55 +00:00
|
|
|
ret = __write_inode(trans, target, target_snapshot);
|
2021-04-20 04:15:44 +00:00
|
|
|
if (ret)
|
|
|
|
goto err;
|
2017-03-17 06:18:50 +00:00
|
|
|
}
|
|
|
|
|
2023-10-25 00:44:36 +00:00
|
|
|
if (fsck_err_on(!backpointer_exists,
|
|
|
|
c, inode_wrong_backpointer,
|
2021-10-20 21:59:38 +00:00
|
|
|
"inode %llu:%u has wrong backpointer:\n"
|
2021-04-20 04:15:44 +00:00
|
|
|
"got %llu:%llu\n"
|
|
|
|
"should be %llu:%llu",
|
2021-10-20 21:59:38 +00:00
|
|
|
target->bi_inum, target_snapshot,
|
2021-04-20 04:15:44 +00:00
|
|
|
target->bi_dir,
|
|
|
|
target->bi_dir_offset,
|
|
|
|
d.k->p.inode,
|
|
|
|
d.k->p.offset)) {
|
|
|
|
target->bi_dir = d.k->p.inode;
|
|
|
|
target->bi_dir_offset = d.k->p.offset;
|
|
|
|
|
2021-10-28 20:16:55 +00:00
|
|
|
ret = __write_inode(trans, target, target_snapshot);
|
2021-04-20 04:15:44 +00:00
|
|
|
if (ret)
|
|
|
|
goto err;
|
2017-03-17 06:18:50 +00:00
|
|
|
}
|
2021-04-20 04:15:44 +00:00
|
|
|
}
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2023-10-25 00:44:36 +00:00
|
|
|
if (fsck_err_on(d.v->d_type != inode_d_type(target),
|
|
|
|
c, dirent_d_type_wrong,
|
2021-10-28 20:16:55 +00:00
|
|
|
"incorrect d_type: got %s, should be %s:\n%s",
|
|
|
|
bch2_d_type_str(d.v->d_type),
|
|
|
|
bch2_d_type_str(inode_d_type(target)),
|
2022-02-25 18:18:19 +00:00
|
|
|
(printbuf_reset(&buf),
|
|
|
|
bch2_bkey_val_to_text(&buf, c, d.s_c), buf.buf))) {
|
2021-10-28 20:16:55 +00:00
|
|
|
n = bch2_trans_kmalloc(trans, bkey_bytes(d.k));
|
|
|
|
ret = PTR_ERR_OR_ZERO(n);
|
|
|
|
if (ret)
|
2022-02-25 18:18:19 +00:00
|
|
|
goto err;
|
2021-04-20 04:15:44 +00:00
|
|
|
|
|
|
|
bkey_reassemble(&n->k_i, d.s_c);
|
2021-10-28 20:16:55 +00:00
|
|
|
n->v.d_type = inode_d_type(target);
|
2021-04-20 04:15:44 +00:00
|
|
|
|
2021-10-28 20:16:55 +00:00
|
|
|
ret = bch2_trans_update(trans, iter, &n->k_i, 0);
|
|
|
|
if (ret)
|
2022-02-25 18:18:19 +00:00
|
|
|
goto err;
|
2021-10-12 16:06:02 +00:00
|
|
|
|
2021-10-28 20:16:55 +00:00
|
|
|
d = dirent_i_to_s_c(n);
|
2021-10-12 16:06:02 +00:00
|
|
|
}
|
|
|
|
|
2024-01-04 04:29:02 +00:00
|
|
|
if (fsck_err_on(d.v->d_type == DT_SUBVOL &&
|
|
|
|
target->bi_parent_subvol != le32_to_cpu(d.v->d_parent_subvol),
|
|
|
|
c, dirent_d_parent_subvol_wrong,
|
|
|
|
"dirent has wrong d_parent_subvol field: got %u, should be %u",
|
|
|
|
le32_to_cpu(d.v->d_parent_subvol),
|
|
|
|
target->bi_parent_subvol)) {
|
2021-10-28 20:16:55 +00:00
|
|
|
n = bch2_trans_kmalloc(trans, bkey_bytes(d.k));
|
|
|
|
ret = PTR_ERR_OR_ZERO(n);
|
|
|
|
if (ret)
|
2022-02-25 18:18:19 +00:00
|
|
|
goto err;
|
2021-10-12 16:06:02 +00:00
|
|
|
|
|
|
|
bkey_reassemble(&n->k_i, d.s_c);
|
|
|
|
n->v.d_parent_subvol = cpu_to_le32(target->bi_parent_subvol);
|
|
|
|
|
2021-10-28 20:16:55 +00:00
|
|
|
ret = bch2_trans_update(trans, iter, &n->k_i, 0);
|
|
|
|
if (ret)
|
2022-02-25 18:18:19 +00:00
|
|
|
goto err;
|
2021-10-12 16:06:02 +00:00
|
|
|
|
2021-10-28 20:16:55 +00:00
|
|
|
d = dirent_i_to_s_c(n);
|
2017-03-17 06:18:50 +00:00
|
|
|
}
|
2022-02-25 18:18:19 +00:00
|
|
|
out:
|
2021-04-20 04:15:44 +00:00
|
|
|
err:
|
2017-03-17 06:18:50 +00:00
|
|
|
fsck_err:
|
2023-11-14 02:24:24 +00:00
|
|
|
bch2_trans_iter_exit(trans, &bp_iter);
|
2022-02-25 18:18:19 +00:00
|
|
|
printbuf_exit(&buf);
|
2023-09-26 20:02:06 +00:00
|
|
|
bch_err_fn(c, ret);
|
2021-04-20 04:15:44 +00:00
|
|
|
return ret;
|
2017-03-17 06:18:50 +00:00
|
|
|
}
|
|
|
|
|
2021-07-15 00:28:27 +00:00
|
|
|
static int check_dirent(struct btree_trans *trans, struct btree_iter *iter,
|
2022-07-16 00:51:09 +00:00
|
|
|
struct bkey_s_c k,
|
2021-07-15 00:28:27 +00:00
|
|
|
struct bch_hash_info *hash_info,
|
2021-04-20 04:15:44 +00:00
|
|
|
struct inode_walker *dir,
|
|
|
|
struct inode_walker *target,
|
|
|
|
struct snapshots_seen *s)
|
2017-03-17 06:18:50 +00:00
|
|
|
{
|
2021-07-15 00:28:27 +00:00
|
|
|
struct bch_fs *c = trans->c;
|
|
|
|
struct bkey_s_c_dirent d;
|
2021-04-20 04:15:44 +00:00
|
|
|
struct inode_walker_entry *i;
|
2022-02-25 18:18:19 +00:00
|
|
|
struct printbuf buf = PRINTBUF;
|
2022-07-14 06:47:36 +00:00
|
|
|
struct bpos equiv;
|
2022-02-25 18:18:19 +00:00
|
|
|
int ret = 0;
|
2018-07-12 23:19:41 +00:00
|
|
|
|
2021-04-20 04:15:44 +00:00
|
|
|
ret = check_key_has_snapshot(trans, iter, k);
|
2022-02-25 18:18:19 +00:00
|
|
|
if (ret) {
|
|
|
|
ret = ret < 0 ? ret : 0;
|
|
|
|
goto out;
|
|
|
|
}
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2022-07-14 06:47:36 +00:00
|
|
|
equiv = k.k->p;
|
|
|
|
equiv.snapshot = bch2_snapshot_equiv(c, k.k->p.snapshot);
|
|
|
|
|
|
|
|
ret = snapshots_seen_update(c, s, iter->btree_id, k.k->p);
|
2021-07-15 00:28:27 +00:00
|
|
|
if (ret)
|
2022-02-25 18:18:19 +00:00
|
|
|
goto err;
|
2021-04-07 00:15:26 +00:00
|
|
|
|
2021-04-20 04:15:44 +00:00
|
|
|
if (k.k->type == KEY_TYPE_whiteout)
|
2022-02-25 18:18:19 +00:00
|
|
|
goto out;
|
2021-04-20 04:15:44 +00:00
|
|
|
|
2023-07-16 18:24:36 +00:00
|
|
|
if (dir->last_pos.inode != k.k->p.inode) {
|
2021-04-20 04:15:44 +00:00
|
|
|
ret = check_subdir_count(trans, dir);
|
|
|
|
if (ret)
|
2022-02-25 18:18:19 +00:00
|
|
|
goto err;
|
2021-04-20 04:15:44 +00:00
|
|
|
}
|
|
|
|
|
2023-12-04 05:39:38 +00:00
|
|
|
BUG_ON(!btree_iter_path(trans, iter)->should_be_locked);
|
2022-04-06 18:35:10 +00:00
|
|
|
|
2023-07-16 18:19:08 +00:00
|
|
|
i = walk_inode(trans, dir, equiv, k.k->type == KEY_TYPE_whiteout);
|
2023-06-25 20:35:49 +00:00
|
|
|
ret = PTR_ERR_OR_ZERO(i);
|
2021-04-20 04:15:44 +00:00
|
|
|
if (ret < 0)
|
2022-02-25 18:18:19 +00:00
|
|
|
goto err;
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2023-07-20 23:30:53 +00:00
|
|
|
if (dir->first_this_inode && dir->inodes.nr)
|
2023-04-25 18:32:39 +00:00
|
|
|
*hash_info = bch2_hash_info_init(c, &dir->inodes.data[0].inode);
|
|
|
|
dir->first_this_inode = false;
|
|
|
|
|
2023-10-25 00:44:36 +00:00
|
|
|
if (fsck_err_on(!i, c, dirent_in_missing_dir_inode,
|
2021-07-15 00:28:27 +00:00
|
|
|
"dirent in nonexisting directory:\n%s",
|
2022-02-25 18:18:19 +00:00
|
|
|
(printbuf_reset(&buf),
|
|
|
|
bch2_bkey_val_to_text(&buf, c, k), buf.buf))) {
|
|
|
|
ret = bch2_btree_delete_at(trans, iter,
|
2021-10-28 20:16:55 +00:00
|
|
|
BTREE_UPDATE_INTERNAL_SNAPSHOT_NODE);
|
2022-02-25 18:18:19 +00:00
|
|
|
goto out;
|
|
|
|
}
|
2021-04-20 04:15:44 +00:00
|
|
|
|
2023-06-25 20:35:49 +00:00
|
|
|
if (!i)
|
2022-02-25 18:18:19 +00:00
|
|
|
goto out;
|
2021-04-20 04:15:44 +00:00
|
|
|
|
2023-10-25 00:44:36 +00:00
|
|
|
if (fsck_err_on(!S_ISDIR(i->inode.bi_mode),
|
|
|
|
c, dirent_in_non_dir_inode,
|
2021-10-28 20:16:55 +00:00
|
|
|
"dirent in non directory inode type %s:\n%s",
|
|
|
|
bch2_d_type_str(inode_d_type(&i->inode)),
|
2022-02-25 18:18:19 +00:00
|
|
|
(printbuf_reset(&buf),
|
|
|
|
bch2_bkey_val_to_text(&buf, c, k), buf.buf))) {
|
|
|
|
ret = bch2_btree_delete_at(trans, iter, 0);
|
|
|
|
goto out;
|
|
|
|
}
|
2021-04-07 00:15:26 +00:00
|
|
|
|
2023-04-25 18:32:39 +00:00
|
|
|
ret = hash_check_key(trans, bch2_dirent_hash_desc, hash_info, iter, k);
|
2021-07-15 00:28:27 +00:00
|
|
|
if (ret < 0)
|
2022-02-25 18:18:19 +00:00
|
|
|
goto err;
|
|
|
|
if (ret) {
|
|
|
|
/* dirent has been deleted */
|
|
|
|
ret = 0;
|
|
|
|
goto out;
|
|
|
|
}
|
2021-04-07 05:55:57 +00:00
|
|
|
|
2021-07-15 00:28:27 +00:00
|
|
|
if (k.k->type != KEY_TYPE_dirent)
|
2022-02-25 18:18:19 +00:00
|
|
|
goto out;
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2021-07-15 00:28:27 +00:00
|
|
|
d = bkey_s_c_to_dirent(k);
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2021-10-12 16:06:02 +00:00
|
|
|
if (d.v->d_type == DT_SUBVOL) {
|
|
|
|
struct bch_inode_unpacked subvol_root;
|
|
|
|
u32 target_subvol = le32_to_cpu(d.v->d_child_subvol);
|
|
|
|
u32 target_snapshot;
|
|
|
|
u64 target_inum;
|
2021-03-16 04:46:26 +00:00
|
|
|
|
2023-12-11 03:52:43 +00:00
|
|
|
ret = subvol_lookup(trans, target_subvol,
|
2021-10-12 16:06:02 +00:00
|
|
|
&target_snapshot, &target_inum);
|
2023-05-27 23:59:59 +00:00
|
|
|
if (ret && !bch2_err_matches(ret, ENOENT))
|
2022-02-25 18:18:19 +00:00
|
|
|
goto err;
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2023-10-25 00:44:36 +00:00
|
|
|
if (fsck_err_on(ret, c, dirent_to_missing_subvol,
|
2023-07-07 02:47:42 +00:00
|
|
|
"dirent points to missing subvolume %u",
|
|
|
|
le32_to_cpu(d.v->d_child_subvol))) {
|
2022-02-25 18:18:19 +00:00
|
|
|
ret = __remove_dirent(trans, d.k->p);
|
|
|
|
goto err;
|
|
|
|
}
|
2021-07-15 00:28:27 +00:00
|
|
|
|
2023-12-11 03:52:43 +00:00
|
|
|
ret = lookup_inode(trans, target_inum,
|
2021-04-20 04:15:44 +00:00
|
|
|
&subvol_root, &target_snapshot);
|
2023-05-27 23:59:59 +00:00
|
|
|
if (ret && !bch2_err_matches(ret, ENOENT))
|
2022-02-25 18:18:19 +00:00
|
|
|
goto err;
|
2021-07-15 00:28:27 +00:00
|
|
|
|
2023-10-25 00:44:36 +00:00
|
|
|
if (fsck_err_on(ret, c, subvol_to_missing_root,
|
2021-04-20 04:15:44 +00:00
|
|
|
"subvolume %u points to missing subvolume root %llu",
|
|
|
|
target_subvol,
|
|
|
|
target_inum)) {
|
|
|
|
bch_err(c, "repair not implemented yet");
|
2022-02-25 18:18:19 +00:00
|
|
|
ret = -EINVAL;
|
|
|
|
goto err;
|
2021-04-20 04:15:44 +00:00
|
|
|
}
|
2021-07-15 00:28:27 +00:00
|
|
|
|
2023-10-25 00:44:36 +00:00
|
|
|
if (fsck_err_on(subvol_root.bi_subvol != target_subvol,
|
|
|
|
c, subvol_root_wrong_bi_subvol,
|
2021-04-20 04:15:44 +00:00
|
|
|
"subvol root %llu has wrong bi_subvol field: got %u, should be %u",
|
|
|
|
target_inum,
|
|
|
|
subvol_root.bi_subvol, target_subvol)) {
|
|
|
|
subvol_root.bi_subvol = target_subvol;
|
2021-10-28 20:16:55 +00:00
|
|
|
ret = __write_inode(trans, &subvol_root, target_snapshot);
|
2021-04-20 04:15:44 +00:00
|
|
|
if (ret)
|
2022-02-25 18:18:19 +00:00
|
|
|
goto err;
|
2021-04-20 04:15:44 +00:00
|
|
|
}
|
2021-07-15 00:28:27 +00:00
|
|
|
|
2021-04-20 04:15:44 +00:00
|
|
|
ret = check_dirent_target(trans, iter, d, &subvol_root,
|
|
|
|
target_snapshot);
|
2021-07-15 00:28:27 +00:00
|
|
|
if (ret)
|
2022-02-25 18:18:19 +00:00
|
|
|
goto err;
|
2021-04-20 04:15:44 +00:00
|
|
|
} else {
|
2021-10-12 16:06:02 +00:00
|
|
|
ret = __get_visible_inodes(trans, target, s, le64_to_cpu(d.v->d_inum));
|
2021-04-20 04:15:44 +00:00
|
|
|
if (ret)
|
2022-02-25 18:18:19 +00:00
|
|
|
goto err;
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2023-10-25 00:44:36 +00:00
|
|
|
if (fsck_err_on(!target->inodes.nr,
|
|
|
|
c, dirent_to_missing_inode,
|
2022-07-14 06:47:36 +00:00
|
|
|
"dirent points to missing inode: (equiv %u)\n%s",
|
|
|
|
equiv.snapshot,
|
2022-02-25 18:18:19 +00:00
|
|
|
(printbuf_reset(&buf),
|
|
|
|
bch2_bkey_val_to_text(&buf, c, k),
|
|
|
|
buf.buf))) {
|
2021-10-28 20:16:55 +00:00
|
|
|
ret = __remove_dirent(trans, d.k->p);
|
2021-04-20 04:15:44 +00:00
|
|
|
if (ret)
|
2022-02-25 18:18:19 +00:00
|
|
|
goto err;
|
2017-03-17 06:18:50 +00:00
|
|
|
}
|
|
|
|
|
2022-03-29 19:48:45 +00:00
|
|
|
darray_for_each(target->inodes, i) {
|
2021-04-20 04:15:44 +00:00
|
|
|
ret = check_dirent_target(trans, iter, d,
|
|
|
|
&i->inode, i->snapshot);
|
|
|
|
if (ret)
|
2022-02-25 18:18:19 +00:00
|
|
|
goto err;
|
2021-04-07 07:11:07 +00:00
|
|
|
}
|
2021-07-15 00:28:27 +00:00
|
|
|
}
|
2021-04-07 07:11:07 +00:00
|
|
|
|
2021-04-20 04:15:44 +00:00
|
|
|
if (d.v->d_type == DT_DIR)
|
2022-07-14 06:47:36 +00:00
|
|
|
for_each_visible_inode(c, s, dir, equiv.snapshot, i)
|
2021-04-20 04:15:44 +00:00
|
|
|
i->count++;
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2022-02-25 18:18:19 +00:00
|
|
|
out:
|
|
|
|
err:
|
2021-07-15 00:28:27 +00:00
|
|
|
fsck_err:
|
2022-02-25 18:18:19 +00:00
|
|
|
printbuf_exit(&buf);
|
2023-09-26 20:02:06 +00:00
|
|
|
bch_err_fn(c, ret);
|
2021-07-15 00:28:27 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2021-07-15 00:28:27 +00:00
|
|
|
/*
|
|
|
|
* Walk dirents: verify that they all have a corresponding S_ISDIR inode,
|
|
|
|
* validate d_type
|
|
|
|
*/
|
2023-07-07 06:42:28 +00:00
|
|
|
int bch2_check_dirents(struct bch_fs *c)
|
2021-07-15 00:28:27 +00:00
|
|
|
{
|
2021-04-20 04:15:44 +00:00
|
|
|
struct inode_walker dir = inode_walker_init();
|
|
|
|
struct inode_walker target = inode_walker_init();
|
|
|
|
struct snapshots_seen s;
|
2021-07-15 00:28:27 +00:00
|
|
|
struct bch_hash_info hash_info;
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2021-04-20 04:15:44 +00:00
|
|
|
snapshots_seen_init(&s);
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2023-12-17 08:07:26 +00:00
|
|
|
int ret = bch2_trans_run(c,
|
|
|
|
for_each_btree_key_commit(trans, iter, BTREE_ID_dirents,
|
|
|
|
POS(BCACHEFS_ROOT_INO, 0),
|
|
|
|
BTREE_ITER_PREFETCH|BTREE_ITER_ALL_SNAPSHOTS,
|
|
|
|
k,
|
|
|
|
NULL, NULL,
|
|
|
|
BCH_TRANS_COMMIT_no_enospc,
|
|
|
|
check_dirent(trans, &iter, k, &hash_info, &dir, &target, &s)));
|
2021-07-15 00:28:27 +00:00
|
|
|
|
2021-04-20 04:15:44 +00:00
|
|
|
snapshots_seen_exit(&s);
|
|
|
|
inode_walker_exit(&dir);
|
|
|
|
inode_walker_exit(&target);
|
2023-09-26 20:02:06 +00:00
|
|
|
bch_err_fn(c, ret);
|
2021-04-20 04:15:44 +00:00
|
|
|
return ret;
|
2017-03-17 06:18:50 +00:00
|
|
|
}
|
|
|
|
|
2021-10-28 20:16:55 +00:00
|
|
|
static int check_xattr(struct btree_trans *trans, struct btree_iter *iter,
|
2022-07-16 00:51:09 +00:00
|
|
|
struct bkey_s_c k,
|
2021-10-28 20:16:55 +00:00
|
|
|
struct bch_hash_info *hash_info,
|
|
|
|
struct inode_walker *inode)
|
|
|
|
{
|
|
|
|
struct bch_fs *c = trans->c;
|
2023-06-25 20:35:49 +00:00
|
|
|
struct inode_walker_entry *i;
|
2021-10-28 20:16:55 +00:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = check_key_has_snapshot(trans, iter, k);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2023-07-16 18:19:08 +00:00
|
|
|
i = walk_inode(trans, inode, k.k->p, k.k->type == KEY_TYPE_whiteout);
|
2023-06-25 20:35:49 +00:00
|
|
|
ret = PTR_ERR_OR_ZERO(i);
|
|
|
|
if (ret)
|
2021-10-28 20:16:55 +00:00
|
|
|
return ret;
|
|
|
|
|
2023-07-20 23:30:53 +00:00
|
|
|
if (inode->first_this_inode && inode->inodes.nr)
|
2023-04-25 18:32:39 +00:00
|
|
|
*hash_info = bch2_hash_info_init(c, &inode->inodes.data[0].inode);
|
|
|
|
inode->first_this_inode = false;
|
|
|
|
|
2023-10-25 00:44:36 +00:00
|
|
|
if (fsck_err_on(!i, c, xattr_in_missing_inode,
|
2021-10-28 20:16:55 +00:00
|
|
|
"xattr for missing inode %llu",
|
|
|
|
k.k->p.inode))
|
|
|
|
return bch2_btree_delete_at(trans, iter, 0);
|
|
|
|
|
2023-06-25 20:35:49 +00:00
|
|
|
if (!i)
|
2021-10-28 20:16:55 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
ret = hash_check_key(trans, bch2_xattr_hash_desc, hash_info, iter, k);
|
|
|
|
fsck_err:
|
2023-09-26 20:02:06 +00:00
|
|
|
bch_err_fn(c, ret);
|
2021-10-28 20:16:55 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-03-17 06:18:50 +00:00
|
|
|
/*
|
|
|
|
* Walk xattrs: verify that they all have a corresponding inode
|
|
|
|
*/
|
2023-07-07 06:42:28 +00:00
|
|
|
int bch2_check_xattrs(struct bch_fs *c)
|
2017-03-17 06:18:50 +00:00
|
|
|
{
|
2021-10-28 20:16:55 +00:00
|
|
|
struct inode_walker inode = inode_walker_init();
|
2021-04-07 05:55:57 +00:00
|
|
|
struct bch_hash_info hash_info;
|
2017-03-17 06:18:50 +00:00
|
|
|
int ret = 0;
|
|
|
|
|
2023-09-12 21:16:02 +00:00
|
|
|
ret = bch2_trans_run(c,
|
|
|
|
for_each_btree_key_commit(trans, iter, BTREE_ID_xattrs,
|
2022-07-16 00:51:09 +00:00
|
|
|
POS(BCACHEFS_ROOT_INO, 0),
|
|
|
|
BTREE_ITER_PREFETCH|BTREE_ITER_ALL_SNAPSHOTS,
|
|
|
|
k,
|
|
|
|
NULL, NULL,
|
2023-11-28 21:36:54 +00:00
|
|
|
BCH_TRANS_COMMIT_no_enospc,
|
2023-09-12 21:16:02 +00:00
|
|
|
check_xattr(trans, &iter, k, &hash_info, &inode)));
|
2023-09-26 20:02:06 +00:00
|
|
|
bch_err_fn(c, ret);
|
2021-10-19 19:08:00 +00:00
|
|
|
return ret;
|
2017-03-17 06:18:50 +00:00
|
|
|
}
|
|
|
|
|
2021-10-28 20:16:55 +00:00
|
|
|
static int check_root_trans(struct btree_trans *trans)
|
2017-03-17 06:18:50 +00:00
|
|
|
{
|
2021-10-28 20:16:55 +00:00
|
|
|
struct bch_fs *c = trans->c;
|
2021-04-20 04:15:44 +00:00
|
|
|
struct bch_inode_unpacked root_inode;
|
2021-04-07 07:11:07 +00:00
|
|
|
u32 snapshot;
|
2021-04-20 04:15:44 +00:00
|
|
|
u64 inum;
|
2017-03-17 06:18:50 +00:00
|
|
|
int ret;
|
|
|
|
|
2023-12-11 03:52:43 +00:00
|
|
|
ret = subvol_lookup(trans, BCACHEFS_ROOT_SUBVOL, &snapshot, &inum);
|
2023-05-27 23:59:59 +00:00
|
|
|
if (ret && !bch2_err_matches(ret, ENOENT))
|
2017-03-17 06:18:50 +00:00
|
|
|
return ret;
|
|
|
|
|
2023-10-25 00:44:36 +00:00
|
|
|
if (mustfix_fsck_err_on(ret, c, root_subvol_missing,
|
|
|
|
"root subvol missing")) {
|
2021-04-20 04:15:44 +00:00
|
|
|
struct bkey_i_subvolume root_subvol;
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2021-04-20 04:15:44 +00:00
|
|
|
snapshot = U32_MAX;
|
|
|
|
inum = BCACHEFS_ROOT_INO;
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2021-04-20 04:15:44 +00:00
|
|
|
bkey_subvolume_init(&root_subvol.k_i);
|
|
|
|
root_subvol.k.p.offset = BCACHEFS_ROOT_SUBVOL;
|
|
|
|
root_subvol.v.flags = 0;
|
|
|
|
root_subvol.v.snapshot = cpu_to_le32(snapshot);
|
|
|
|
root_subvol.v.inode = cpu_to_le64(inum);
|
2023-12-11 03:51:16 +00:00
|
|
|
ret = bch2_btree_insert_trans(trans, BTREE_ID_subvolumes, &root_subvol.k_i, 0);
|
2023-09-26 20:02:06 +00:00
|
|
|
bch_err_msg(c, ret, "writing root subvol");
|
|
|
|
if (ret)
|
2021-04-20 04:15:44 +00:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2023-12-11 03:52:43 +00:00
|
|
|
ret = lookup_inode(trans, BCACHEFS_ROOT_INO, &root_inode, &snapshot);
|
2023-05-27 23:59:59 +00:00
|
|
|
if (ret && !bch2_err_matches(ret, ENOENT))
|
2021-04-20 04:15:44 +00:00
|
|
|
return ret;
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2023-10-25 00:44:36 +00:00
|
|
|
if (mustfix_fsck_err_on(ret, c, root_dir_missing,
|
|
|
|
"root directory missing") ||
|
|
|
|
mustfix_fsck_err_on(!S_ISDIR(root_inode.bi_mode),
|
|
|
|
c, root_inode_not_dir,
|
2021-04-20 04:15:44 +00:00
|
|
|
"root inode not a directory")) {
|
|
|
|
bch2_inode_init(c, &root_inode, 0, 0, S_IFDIR|0755,
|
|
|
|
0, NULL);
|
|
|
|
root_inode.bi_inum = inum;
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2021-10-28 20:16:55 +00:00
|
|
|
ret = __write_inode(trans, &root_inode, snapshot);
|
2023-09-26 20:02:06 +00:00
|
|
|
bch_err_msg(c, ret, "writing root inode");
|
2021-04-20 04:15:44 +00:00
|
|
|
}
|
|
|
|
err:
|
|
|
|
fsck_err:
|
|
|
|
return ret;
|
2017-03-17 06:18:50 +00:00
|
|
|
}
|
|
|
|
|
2021-10-28 20:16:55 +00:00
|
|
|
/* Get root directory, create if it doesn't exist: */
|
2023-07-07 06:42:28 +00:00
|
|
|
int bch2_check_root(struct bch_fs *c)
|
2021-10-28 20:16:55 +00:00
|
|
|
{
|
2023-12-11 03:51:16 +00:00
|
|
|
int ret = bch2_trans_do(c, NULL, NULL, BCH_TRANS_COMMIT_no_enospc,
|
2023-09-12 21:16:02 +00:00
|
|
|
check_root_trans(trans));
|
2023-09-26 20:02:06 +00:00
|
|
|
bch_err_fn(c, ret);
|
2023-06-20 17:49:25 +00:00
|
|
|
return ret;
|
2021-10-28 20:16:55 +00:00
|
|
|
}
|
|
|
|
|
2022-03-29 19:48:45 +00:00
|
|
|
struct pathbuf_entry {
|
|
|
|
u64 inum;
|
|
|
|
u32 snapshot;
|
2017-03-17 06:18:50 +00:00
|
|
|
};
|
|
|
|
|
2022-03-29 19:48:45 +00:00
|
|
|
typedef DARRAY(struct pathbuf_entry) pathbuf;
|
|
|
|
|
|
|
|
static bool path_is_dup(pathbuf *p, u64 inum, u32 snapshot)
|
2021-10-20 21:59:38 +00:00
|
|
|
{
|
2022-03-29 19:48:45 +00:00
|
|
|
darray_for_each(*p, i)
|
2021-10-20 21:59:38 +00:00
|
|
|
if (i->inum == inum &&
|
|
|
|
i->snapshot == snapshot)
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-03-29 19:48:45 +00:00
|
|
|
static int path_down(struct bch_fs *c, pathbuf *p,
|
2021-12-31 01:14:52 +00:00
|
|
|
u64 inum, u32 snapshot)
|
2017-03-17 06:18:50 +00:00
|
|
|
{
|
2022-03-29 19:48:45 +00:00
|
|
|
int ret = darray_push(p, ((struct pathbuf_entry) {
|
2021-10-20 21:59:38 +00:00
|
|
|
.inum = inum,
|
|
|
|
.snapshot = snapshot,
|
2022-03-29 19:48:45 +00:00
|
|
|
}));
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
bch_err(c, "fsck: error allocating memory for pathbuf, size %zu",
|
|
|
|
p->size);
|
|
|
|
return ret;
|
2017-03-17 06:18:50 +00:00
|
|
|
}
|
|
|
|
|
2021-10-20 21:59:38 +00:00
|
|
|
/*
|
|
|
|
* Check that a given inode is reachable from the root:
|
|
|
|
*
|
|
|
|
* XXX: we should also be verifying that inodes are in the right subvolumes
|
|
|
|
*/
|
2021-04-07 07:11:07 +00:00
|
|
|
static int check_path(struct btree_trans *trans,
|
2022-03-29 19:48:45 +00:00
|
|
|
pathbuf *p,
|
2021-04-20 03:31:40 +00:00
|
|
|
struct bch_inode_unpacked *inode,
|
|
|
|
u32 snapshot)
|
2017-03-17 06:18:50 +00:00
|
|
|
{
|
2021-04-07 07:11:07 +00:00
|
|
|
struct bch_fs *c = trans->c;
|
2017-03-17 06:18:50 +00:00
|
|
|
int ret = 0;
|
|
|
|
|
2022-07-14 06:47:36 +00:00
|
|
|
snapshot = bch2_snapshot_equiv(c, snapshot);
|
2021-04-07 07:11:07 +00:00
|
|
|
p->nr = 0;
|
2019-03-25 19:10:15 +00:00
|
|
|
|
2021-10-19 05:08:05 +00:00
|
|
|
while (!(inode->bi_inum == BCACHEFS_ROOT_INO &&
|
|
|
|
inode->bi_subvol == BCACHEFS_ROOT_SUBVOL)) {
|
2021-10-12 16:06:02 +00:00
|
|
|
struct btree_iter dirent_iter;
|
|
|
|
struct bkey_s_c_dirent d;
|
2021-10-20 21:59:38 +00:00
|
|
|
u32 parent_snapshot = snapshot;
|
|
|
|
|
2021-10-12 16:06:02 +00:00
|
|
|
if (inode->bi_subvol) {
|
2021-10-19 05:08:05 +00:00
|
|
|
u64 inum;
|
|
|
|
|
|
|
|
ret = subvol_lookup(trans, inode->bi_parent_subvol,
|
2021-10-20 21:59:38 +00:00
|
|
|
&parent_snapshot, &inum);
|
2021-10-19 05:08:05 +00:00
|
|
|
if (ret)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2023-12-11 03:52:43 +00:00
|
|
|
d = dirent_get_by_pos(trans, &dirent_iter,
|
|
|
|
SPOS(inode->bi_dir, inode->bi_dir_offset,
|
|
|
|
parent_snapshot));
|
|
|
|
ret = bkey_err(d.s_c);
|
2023-05-27 23:59:59 +00:00
|
|
|
if (ret && !bch2_err_matches(ret, ENOENT))
|
2021-04-07 07:11:07 +00:00
|
|
|
break;
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2021-10-12 16:06:02 +00:00
|
|
|
if (!ret && !dirent_points_to_inode(d, inode)) {
|
|
|
|
bch2_trans_iter_exit(trans, &dirent_iter);
|
2023-05-27 23:59:59 +00:00
|
|
|
ret = -BCH_ERR_ENOENT_dirent_doesnt_match_inode;
|
2021-10-12 16:06:02 +00:00
|
|
|
}
|
|
|
|
|
2023-05-27 23:59:59 +00:00
|
|
|
if (bch2_err_matches(ret, ENOENT)) {
|
2023-10-25 00:44:36 +00:00
|
|
|
if (fsck_err(c, inode_unreachable,
|
|
|
|
"unreachable inode %llu:%u, type %s nlink %u backptr %llu:%llu",
|
2021-04-20 04:15:44 +00:00
|
|
|
inode->bi_inum, snapshot,
|
2021-10-28 20:16:55 +00:00
|
|
|
bch2_d_type_str(inode_d_type(inode)),
|
2021-04-07 07:11:07 +00:00
|
|
|
inode->bi_nlink,
|
|
|
|
inode->bi_dir,
|
|
|
|
inode->bi_dir_offset))
|
2021-04-20 03:31:40 +00:00
|
|
|
ret = reattach_inode(trans, inode, snapshot);
|
2021-04-07 07:11:07 +00:00
|
|
|
break;
|
|
|
|
}
|
2021-10-12 16:06:02 +00:00
|
|
|
|
|
|
|
bch2_trans_iter_exit(trans, &dirent_iter);
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2021-04-07 07:11:07 +00:00
|
|
|
if (!S_ISDIR(inode->bi_mode))
|
|
|
|
break;
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2021-12-31 01:14:52 +00:00
|
|
|
ret = path_down(c, p, inode->bi_inum, snapshot);
|
2021-04-07 07:11:07 +00:00
|
|
|
if (ret) {
|
|
|
|
bch_err(c, "memory allocation failure");
|
|
|
|
return ret;
|
|
|
|
}
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2021-10-20 21:59:38 +00:00
|
|
|
snapshot = parent_snapshot;
|
|
|
|
|
|
|
|
ret = lookup_inode(trans, inode->bi_dir, inode, &snapshot);
|
|
|
|
if (ret) {
|
|
|
|
/* Should have been caught in dirents pass */
|
2023-12-11 03:52:43 +00:00
|
|
|
if (!bch2_err_matches(ret, BCH_ERR_transaction_restart))
|
|
|
|
bch_err(c, "error looking up parent directory: %i", ret);
|
2021-10-20 21:59:38 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (path_is_dup(p, inode->bi_inum, snapshot)) {
|
2021-04-07 07:11:07 +00:00
|
|
|
/* XXX print path */
|
2021-10-20 21:59:38 +00:00
|
|
|
bch_err(c, "directory structure loop");
|
|
|
|
|
2022-03-29 19:48:45 +00:00
|
|
|
darray_for_each(*p, i)
|
2021-10-20 21:59:38 +00:00
|
|
|
pr_err("%llu:%u", i->inum, i->snapshot);
|
|
|
|
pr_err("%llu:%u", inode->bi_inum, snapshot);
|
|
|
|
|
2023-12-11 03:52:43 +00:00
|
|
|
if (!fsck_err(c, dir_loop, "directory structure loop"))
|
2021-04-07 07:11:07 +00:00
|
|
|
return 0;
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2023-12-11 03:52:43 +00:00
|
|
|
ret = remove_backpointer(trans, inode);
|
|
|
|
if (ret && !bch2_err_matches(ret, BCH_ERR_transaction_restart))
|
|
|
|
bch_err_msg(c, ret, "removing dirent");
|
|
|
|
if (ret)
|
2021-04-07 07:11:07 +00:00
|
|
|
break;
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2021-04-20 03:31:40 +00:00
|
|
|
ret = reattach_inode(trans, inode, snapshot);
|
2023-12-11 03:52:43 +00:00
|
|
|
if (ret && !bch2_err_matches(ret, BCH_ERR_transaction_restart))
|
|
|
|
bch_err_msg(c, ret, "reattaching inode %llu", inode->bi_inum);
|
|
|
|
break;
|
2017-03-17 06:18:50 +00:00
|
|
|
}
|
|
|
|
}
|
2021-04-07 07:11:07 +00:00
|
|
|
fsck_err:
|
2023-09-26 20:02:06 +00:00
|
|
|
bch_err_fn(c, ret);
|
2021-04-07 07:11:07 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2021-04-07 07:11:07 +00:00
|
|
|
/*
|
|
|
|
* Check for unreachable inodes, as well as loops in the directory structure:
|
2023-07-07 06:42:28 +00:00
|
|
|
* After bch2_check_dirents(), if an inode backpointer doesn't exist that means it's
|
2021-04-07 07:11:07 +00:00
|
|
|
* unreachable:
|
|
|
|
*/
|
2023-07-07 06:42:28 +00:00
|
|
|
int bch2_check_directory_structure(struct bch_fs *c)
|
2021-04-07 07:11:07 +00:00
|
|
|
{
|
|
|
|
struct bch_inode_unpacked u;
|
2022-03-29 19:48:45 +00:00
|
|
|
pathbuf path = { 0, };
|
2021-04-07 07:11:07 +00:00
|
|
|
int ret;
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2023-12-11 03:52:43 +00:00
|
|
|
ret = bch2_trans_run(c,
|
|
|
|
for_each_btree_key_commit(trans, iter, BTREE_ID_inodes, POS_MIN,
|
|
|
|
BTREE_ITER_INTENT|
|
|
|
|
BTREE_ITER_PREFETCH|
|
|
|
|
BTREE_ITER_ALL_SNAPSHOTS, k,
|
|
|
|
NULL, NULL, BCH_TRANS_COMMIT_no_enospc, ({
|
|
|
|
if (!bkey_is_inode(k.k))
|
|
|
|
continue;
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2023-12-11 03:52:43 +00:00
|
|
|
BUG_ON(bch2_inode_unpack(k, &u));
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2023-12-11 03:52:43 +00:00
|
|
|
if (u.bi_flags & BCH_INODE_unlinked)
|
|
|
|
continue;
|
2021-04-20 04:15:44 +00:00
|
|
|
|
2023-12-11 03:52:43 +00:00
|
|
|
check_path(trans, &path, &u, iter.pos.snapshot);
|
|
|
|
})));
|
2022-03-29 19:48:45 +00:00
|
|
|
darray_exit(&path);
|
2023-12-08 04:28:26 +00:00
|
|
|
|
2023-09-26 20:02:06 +00:00
|
|
|
bch_err_fn(c, ret);
|
2021-10-19 19:08:00 +00:00
|
|
|
return ret;
|
2017-03-17 06:18:50 +00:00
|
|
|
}
|
|
|
|
|
2021-04-22 01:08:49 +00:00
|
|
|
struct nlink_table {
|
|
|
|
size_t nr;
|
|
|
|
size_t size;
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2021-04-22 01:08:49 +00:00
|
|
|
struct nlink {
|
|
|
|
u64 inum;
|
|
|
|
u32 snapshot;
|
|
|
|
u32 count;
|
|
|
|
} *d;
|
|
|
|
};
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2021-12-31 01:14:52 +00:00
|
|
|
static int add_nlink(struct bch_fs *c, struct nlink_table *t,
|
|
|
|
u64 inum, u32 snapshot)
|
2017-03-17 06:18:50 +00:00
|
|
|
{
|
2021-04-22 01:08:49 +00:00
|
|
|
if (t->nr == t->size) {
|
|
|
|
size_t new_size = max_t(size_t, 128UL, t->size * 2);
|
2022-10-19 22:31:33 +00:00
|
|
|
void *d = kvmalloc_array(new_size, sizeof(t->d[0]), GFP_KERNEL);
|
|
|
|
|
2021-04-22 01:08:49 +00:00
|
|
|
if (!d) {
|
2021-12-31 01:14:52 +00:00
|
|
|
bch_err(c, "fsck: error allocating memory for nlink_table, size %zu",
|
|
|
|
new_size);
|
2023-03-14 19:35:57 +00:00
|
|
|
return -BCH_ERR_ENOMEM_fsck_add_nlink;
|
2021-04-22 01:08:49 +00:00
|
|
|
}
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2021-05-17 20:10:06 +00:00
|
|
|
if (t->d)
|
|
|
|
memcpy(d, t->d, t->size * sizeof(t->d[0]));
|
2021-04-22 01:08:49 +00:00
|
|
|
kvfree(t->d);
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2021-04-22 01:08:49 +00:00
|
|
|
t->d = d;
|
|
|
|
t->size = new_size;
|
2021-02-13 01:53:29 +00:00
|
|
|
}
|
|
|
|
|
2021-04-22 01:08:49 +00:00
|
|
|
|
|
|
|
t->d[t->nr++] = (struct nlink) {
|
|
|
|
.inum = inum,
|
|
|
|
.snapshot = snapshot,
|
|
|
|
};
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nlink_cmp(const void *_l, const void *_r)
|
|
|
|
{
|
|
|
|
const struct nlink *l = _l;
|
|
|
|
const struct nlink *r = _r;
|
|
|
|
|
2023-11-14 02:17:19 +00:00
|
|
|
return cmp_int(l->inum, r->inum);
|
2021-04-22 01:08:49 +00:00
|
|
|
}
|
|
|
|
|
2021-04-20 04:15:44 +00:00
|
|
|
static void inc_link(struct bch_fs *c, struct snapshots_seen *s,
|
|
|
|
struct nlink_table *links,
|
|
|
|
u64 range_start, u64 range_end, u64 inum, u32 snapshot)
|
2021-04-22 01:08:49 +00:00
|
|
|
{
|
|
|
|
struct nlink *link, key = {
|
|
|
|
.inum = inum, .snapshot = U32_MAX,
|
|
|
|
};
|
|
|
|
|
|
|
|
if (inum < range_start || inum >= range_end)
|
2017-03-17 06:18:50 +00:00
|
|
|
return;
|
2021-04-22 01:08:49 +00:00
|
|
|
|
|
|
|
link = __inline_bsearch(&key, links->d, links->nr,
|
|
|
|
sizeof(links->d[0]), nlink_cmp);
|
2021-04-20 04:15:44 +00:00
|
|
|
if (!link)
|
|
|
|
return;
|
|
|
|
|
|
|
|
while (link > links->d && link[0].inum == link[-1].inum)
|
|
|
|
--link;
|
|
|
|
|
|
|
|
for (; link < links->d + links->nr && link->inum == inum; link++)
|
|
|
|
if (ref_visible(c, s, snapshot, link->snapshot)) {
|
|
|
|
link->count++;
|
|
|
|
if (link->snapshot >= snapshot)
|
|
|
|
break;
|
|
|
|
}
|
2021-04-22 01:08:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
noinline_for_stack
|
|
|
|
static int check_nlinks_find_hardlinks(struct bch_fs *c,
|
|
|
|
struct nlink_table *t,
|
|
|
|
u64 start, u64 *end)
|
|
|
|
{
|
2023-12-08 04:28:26 +00:00
|
|
|
int ret = bch2_trans_run(c,
|
2023-12-08 04:33:11 +00:00
|
|
|
for_each_btree_key(trans, iter, BTREE_ID_inodes,
|
|
|
|
POS(0, start),
|
|
|
|
BTREE_ITER_INTENT|
|
|
|
|
BTREE_ITER_PREFETCH|
|
|
|
|
BTREE_ITER_ALL_SNAPSHOTS, k, ({
|
2023-12-08 04:28:26 +00:00
|
|
|
if (!bkey_is_inode(k.k))
|
|
|
|
continue;
|
2021-04-22 01:08:49 +00:00
|
|
|
|
2023-12-08 04:28:26 +00:00
|
|
|
/* Should never fail, checked by bch2_inode_invalid: */
|
2023-12-17 03:30:09 +00:00
|
|
|
struct bch_inode_unpacked u;
|
2023-12-08 04:28:26 +00:00
|
|
|
BUG_ON(bch2_inode_unpack(k, &u));
|
2021-04-22 01:08:49 +00:00
|
|
|
|
2023-12-08 04:28:26 +00:00
|
|
|
/*
|
|
|
|
* Backpointer and directory structure checks are sufficient for
|
|
|
|
* directories, since they can't have hardlinks:
|
|
|
|
*/
|
|
|
|
if (S_ISDIR(u.bi_mode))
|
|
|
|
continue;
|
2021-04-22 01:08:49 +00:00
|
|
|
|
2023-12-08 04:28:26 +00:00
|
|
|
if (!u.bi_nlink)
|
|
|
|
continue;
|
2021-04-22 01:08:49 +00:00
|
|
|
|
2023-12-08 04:28:26 +00:00
|
|
|
ret = add_nlink(c, t, k.k->p.offset, k.k->p.snapshot);
|
|
|
|
if (ret) {
|
|
|
|
*end = k.k->p.offset;
|
|
|
|
ret = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
0;
|
|
|
|
})));
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2023-12-08 04:28:26 +00:00
|
|
|
bch_err_fn(c, ret);
|
2021-04-22 01:08:49 +00:00
|
|
|
return ret;
|
2017-03-17 06:18:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
noinline_for_stack
|
2021-04-22 01:08:49 +00:00
|
|
|
static int check_nlinks_walk_dirents(struct bch_fs *c, struct nlink_table *links,
|
|
|
|
u64 range_start, u64 range_end)
|
2017-03-17 06:18:50 +00:00
|
|
|
{
|
2021-04-20 04:15:44 +00:00
|
|
|
struct snapshots_seen s;
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2021-04-20 04:15:44 +00:00
|
|
|
snapshots_seen_init(&s);
|
|
|
|
|
2023-12-08 04:28:26 +00:00
|
|
|
int ret = bch2_trans_run(c,
|
2023-12-08 04:33:11 +00:00
|
|
|
for_each_btree_key(trans, iter, BTREE_ID_dirents, POS_MIN,
|
|
|
|
BTREE_ITER_INTENT|
|
|
|
|
BTREE_ITER_PREFETCH|
|
|
|
|
BTREE_ITER_ALL_SNAPSHOTS, k, ({
|
2023-12-08 04:28:26 +00:00
|
|
|
ret = snapshots_seen_update(c, &s, iter.btree_id, k.k->p);
|
|
|
|
if (ret)
|
|
|
|
break;
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2023-12-17 03:30:09 +00:00
|
|
|
if (k.k->type == KEY_TYPE_dirent) {
|
|
|
|
struct bkey_s_c_dirent d = bkey_s_c_to_dirent(k);
|
2021-03-20 02:34:54 +00:00
|
|
|
|
2023-12-08 04:28:26 +00:00
|
|
|
if (d.v->d_type != DT_DIR &&
|
|
|
|
d.v->d_type != DT_SUBVOL)
|
|
|
|
inc_link(c, &s, links, range_start, range_end,
|
|
|
|
le64_to_cpu(d.v->d_inum),
|
|
|
|
bch2_snapshot_equiv(c, d.k->p.snapshot));
|
|
|
|
}
|
|
|
|
0;
|
|
|
|
})));
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2021-04-20 04:15:44 +00:00
|
|
|
snapshots_seen_exit(&s);
|
2023-12-08 04:28:26 +00:00
|
|
|
|
|
|
|
bch_err_fn(c, ret);
|
2017-03-17 06:18:50 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2022-07-17 04:44:19 +00:00
|
|
|
static int check_nlinks_update_inode(struct btree_trans *trans, struct btree_iter *iter,
|
|
|
|
struct bkey_s_c k,
|
|
|
|
struct nlink_table *links,
|
|
|
|
size_t *idx, u64 range_end)
|
|
|
|
{
|
|
|
|
struct bch_fs *c = trans->c;
|
|
|
|
struct bch_inode_unpacked u;
|
|
|
|
struct nlink *link = &links->d[*idx];
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (k.k->p.offset >= range_end)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
if (!bkey_is_inode(k.k))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
BUG_ON(bch2_inode_unpack(k, &u));
|
|
|
|
|
2023-07-07 02:47:42 +00:00
|
|
|
if (S_ISDIR(u.bi_mode))
|
2022-07-17 04:44:19 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!u.bi_nlink)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
while ((cmp_int(link->inum, k.k->p.offset) ?:
|
|
|
|
cmp_int(link->snapshot, k.k->p.snapshot)) < 0) {
|
|
|
|
BUG_ON(*idx == links->nr);
|
|
|
|
link = &links->d[++*idx];
|
|
|
|
}
|
|
|
|
|
2023-10-25 00:44:36 +00:00
|
|
|
if (fsck_err_on(bch2_inode_nlink_get(&u) != link->count,
|
|
|
|
c, inode_wrong_nlink,
|
2022-07-17 04:44:19 +00:00
|
|
|
"inode %llu type %s has wrong i_nlink (%u, should be %u)",
|
|
|
|
u.bi_inum, bch2_d_types[mode_to_type(u.bi_mode)],
|
|
|
|
bch2_inode_nlink_get(&u), link->count)) {
|
|
|
|
bch2_inode_nlink_set(&u, link->count);
|
|
|
|
ret = __write_inode(trans, &u, k.k->p.snapshot);
|
|
|
|
}
|
|
|
|
fsck_err:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-03-17 06:18:50 +00:00
|
|
|
noinline_for_stack
|
2021-04-22 01:08:49 +00:00
|
|
|
static int check_nlinks_update_hardlinks(struct bch_fs *c,
|
|
|
|
struct nlink_table *links,
|
2017-03-17 06:18:50 +00:00
|
|
|
u64 range_start, u64 range_end)
|
|
|
|
{
|
2022-07-17 04:44:19 +00:00
|
|
|
size_t idx = 0;
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2023-12-17 03:30:09 +00:00
|
|
|
int ret = bch2_trans_run(c,
|
2023-09-12 21:16:02 +00:00
|
|
|
for_each_btree_key_commit(trans, iter, BTREE_ID_inodes,
|
|
|
|
POS(0, range_start),
|
|
|
|
BTREE_ITER_INTENT|BTREE_ITER_PREFETCH|BTREE_ITER_ALL_SNAPSHOTS, k,
|
2023-11-28 21:36:54 +00:00
|
|
|
NULL, NULL, BCH_TRANS_COMMIT_no_enospc,
|
2023-09-12 21:16:02 +00:00
|
|
|
check_nlinks_update_inode(trans, &iter, k, links, &idx, range_end)));
|
2022-07-17 04:44:19 +00:00
|
|
|
if (ret < 0) {
|
2023-11-14 02:24:24 +00:00
|
|
|
bch_err(c, "error in fsck walking inodes: %s", bch2_err_str(ret));
|
2022-07-17 04:44:19 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2022-07-17 04:44:19 +00:00
|
|
|
return 0;
|
2017-03-17 06:18:50 +00:00
|
|
|
}
|
|
|
|
|
2023-07-07 06:42:28 +00:00
|
|
|
int bch2_check_nlinks(struct bch_fs *c)
|
2017-03-17 06:18:50 +00:00
|
|
|
{
|
2021-04-22 01:08:49 +00:00
|
|
|
struct nlink_table links = { 0 };
|
2017-03-17 06:18:50 +00:00
|
|
|
u64 this_iter_range_start, next_iter_range_start = 0;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
do {
|
|
|
|
this_iter_range_start = next_iter_range_start;
|
|
|
|
next_iter_range_start = U64_MAX;
|
|
|
|
|
2021-04-22 01:08:49 +00:00
|
|
|
ret = check_nlinks_find_hardlinks(c, &links,
|
|
|
|
this_iter_range_start,
|
|
|
|
&next_iter_range_start);
|
|
|
|
|
|
|
|
ret = check_nlinks_walk_dirents(c, &links,
|
2017-03-17 06:18:50 +00:00
|
|
|
this_iter_range_start,
|
2021-04-22 01:08:49 +00:00
|
|
|
next_iter_range_start);
|
2017-03-17 06:18:50 +00:00
|
|
|
if (ret)
|
|
|
|
break;
|
|
|
|
|
2021-04-22 01:08:49 +00:00
|
|
|
ret = check_nlinks_update_hardlinks(c, &links,
|
2017-03-17 06:18:50 +00:00
|
|
|
this_iter_range_start,
|
|
|
|
next_iter_range_start);
|
|
|
|
if (ret)
|
|
|
|
break;
|
|
|
|
|
2021-04-22 01:08:49 +00:00
|
|
|
links.nr = 0;
|
2017-03-17 06:18:50 +00:00
|
|
|
} while (next_iter_range_start != U64_MAX);
|
|
|
|
|
2021-04-22 01:08:49 +00:00
|
|
|
kvfree(links.d);
|
2023-09-26 20:02:06 +00:00
|
|
|
bch_err_fn(c, ret);
|
2017-03-17 06:18:50 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2022-07-17 04:44:19 +00:00
|
|
|
static int fix_reflink_p_key(struct btree_trans *trans, struct btree_iter *iter,
|
|
|
|
struct bkey_s_c k)
|
2021-10-19 21:30:16 +00:00
|
|
|
{
|
|
|
|
struct bkey_s_c_reflink_p p;
|
|
|
|
struct bkey_i_reflink_p *u;
|
|
|
|
|
|
|
|
if (k.k->type != KEY_TYPE_reflink_p)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
p = bkey_s_c_to_reflink_p(k);
|
|
|
|
|
2021-10-14 13:54:47 +00:00
|
|
|
if (!p.v->front_pad && !p.v->back_pad)
|
2021-10-19 21:30:16 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
u = bch2_trans_kmalloc(trans, sizeof(*u));
|
2023-12-17 03:43:41 +00:00
|
|
|
int ret = PTR_ERR_OR_ZERO(u);
|
2021-10-19 21:30:16 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
bkey_reassemble(&u->k_i, k);
|
2021-10-14 13:54:47 +00:00
|
|
|
u->v.front_pad = 0;
|
|
|
|
u->v.back_pad = 0;
|
2021-10-19 21:30:16 +00:00
|
|
|
|
2021-10-25 23:30:24 +00:00
|
|
|
return bch2_trans_update(trans, iter, &u->k_i, BTREE_TRIGGER_NORUN);
|
2021-10-19 21:30:16 +00:00
|
|
|
}
|
|
|
|
|
2023-07-07 06:42:28 +00:00
|
|
|
int bch2_fix_reflink_p(struct bch_fs *c)
|
2021-10-19 21:30:16 +00:00
|
|
|
{
|
|
|
|
if (c->sb.version >= bcachefs_metadata_version_reflink_p_fix)
|
|
|
|
return 0;
|
|
|
|
|
2023-12-17 03:30:09 +00:00
|
|
|
int ret = bch2_trans_run(c,
|
2023-09-12 21:16:02 +00:00
|
|
|
for_each_btree_key_commit(trans, iter,
|
2023-06-20 17:49:25 +00:00
|
|
|
BTREE_ID_extents, POS_MIN,
|
|
|
|
BTREE_ITER_INTENT|BTREE_ITER_PREFETCH|
|
|
|
|
BTREE_ITER_ALL_SNAPSHOTS, k,
|
2023-11-28 21:36:54 +00:00
|
|
|
NULL, NULL, BCH_TRANS_COMMIT_no_enospc,
|
2023-09-12 21:16:02 +00:00
|
|
|
fix_reflink_p_key(trans, &iter, k)));
|
2023-09-26 20:02:06 +00:00
|
|
|
bch_err_fn(c, ret);
|
2021-10-19 21:30:16 +00:00
|
|
|
return ret;
|
|
|
|
}
|