/* * XXX: this is handling transaction restarts without returning * -BCH_ERR_transaction_restart_nested, this is not how we do things anymore:
*/ static s64 bch2_count_inode_sectors(struct btree_trans *trans, u64 inum,
u32 snapshot)
{
u64 sectors = 0;
int ret = for_each_btree_key_max(trans, iter, BTREE_ID_extents,
SPOS(inum, 0, snapshot),
POS(inum, U64_MAX),
0, k, ({ if (bkey_extent_is_allocation(k.k))
sectors += k.k->size;
0;
}));
/* * Find any subvolume associated with a tree of snapshots * We can't rely on master_subvol - it might have been deleted.
*/ staticint find_snapshot_tree_subvol(struct btree_trans *trans,
u32 tree_id, u32 *subvol)
{ struct btree_iter iter; struct bkey_s_c k; int ret;
ret = lookup_dirent_in_snapshot(trans, root_hash_info, root_inum,
&lostfound_str, &inum, &d_type, snapshot); if (bch2_err_matches(ret, ENOENT)) goto create_lostfound;
bch_err_fn(c, ret); if (ret) return ret;
if (d_type != DT_DIR) {
bch_err(c, "error looking up lost+found: not a directory"); return bch_err_throw(c, ENOENT_not_directory);
}
/* * The bch2_check_dirents pass has already run, dangling dirents * shouldn't exist here:
*/
ret = bch2_inode_find_by_inum_snapshot(trans, inum, snapshot, lostfound, 0);
bch_err_msg(c, ret, "looking up lost+found %llu:%u in (root inode %llu, snapshot root %u)",
inum, snapshot, root_inum.inum, bch2_snapshot_root(c, snapshot)); return ret;
create_lostfound: /* * we always create lost+found in the root snapshot; we don't want * different branches of the snapshot tree to have different lost+found
*/
snapshot = le32_to_cpu(st.root_snapshot); /* * XXX: we could have a nicer log message here if we had a nice way to * walk backpointers to print a path
*/ struct printbuf path = PRINTBUF;
ret = bch2_inum_to_path(trans, root_inum, &path); if (ret) goto err;
/* * Subvolume roots are special: older versions of subvolume roots may be * disconnected, it's only the newest version that matters. * * We only keep a single dirent pointing to a subvolume root, i.e. * older versions of snapshots will not have a different dirent pointing * to the same subvolume root. * * This is because dirents that point to subvolumes are only visible in * the parent subvolume - versioning is not needed - and keeping them * around would break fsck, because when we're crossing subvolumes we * don't have a consistent snapshot ID to do check the inode <-> dirent * relationships. * * Thus, a subvolume root that's been renamed after a snapshot will have * a disconnected older version - that's expected. * * Note that taking a snapshot always updates the root inode (to update * the dirent backpointer), so a subvolume root inode with * BCH_INODE_has_child_snapshot is never visible.
*/ if (inode->bi_subvol &&
(inode->bi_flags & BCH_INODE_has_child_snapshot)) returnfalse;
staticint maybe_delete_dirent(struct btree_trans *trans, struct bpos d_pos, u32 snapshot)
{ struct btree_iter iter; struct bkey_s_c k = bch2_bkey_get_iter(trans, &iter, BTREE_ID_dirents,
SPOS(d_pos.inode, d_pos.offset, snapshot),
BTREE_ITER_intent|
BTREE_ITER_with_updates); int ret = bkey_err(k); if (ret) return ret;
if (bpos_eq(k.k->p, d_pos)) { /* * delet_at() doesn't work because the update path doesn't * internally use BTREE_ITER_with_updates yet
*/ struct bkey_i *k = bch2_trans_kmalloc(trans, sizeof(*k));
ret = PTR_ERR_OR_ZERO(k); if (ret) goto err;
ret = __bch2_fsck_write_inode(trans, inode); if (ret) return ret;
{ CLASS(printbuf, buf)();
ret = bch2_inum_snapshot_to_path(trans, inode->bi_inum,
inode->bi_snapshot, NULL, &buf); if (ret) return ret;
bch_info(c, "reattached at %s", buf.buf);
}
/* * Fix up inodes in child snapshots: if they should also be reattached * update the backpointer field, if they should not be we need to emit * whiteouts for the dirent we just created.
*/ if (!inode->bi_subvol && bch2_snapshot_is_leaf(c, inode->bi_snapshot) <= 0) {
snapshot_id_list whiteouts_done; struct btree_iter iter; struct bkey_s_c k;
struct bch_inode_unpacked child_inode;
ret = bch2_inode_unpack(k, &child_inode); if (ret) break;
if (!inode_should_reattach(&child_inode)) {
ret = maybe_delete_dirent(trans,
SPOS(lostfound.bi_inum, inode->bi_dir_offset,
dirent_snapshot),
k.k->p.snapshot); if (ret) break;
ret = snapshot_list_add(c, &whiteouts_done, k.k->p.snapshot); if (ret) break;
} else {
iter.snapshot = k.k->p.snapshot;
child_inode.bi_dir = inode->bi_dir;
child_inode.bi_dir_offset = inode->bi_dir_offset;
ret = bch2_inode_write_flags(trans, &iter, &child_inode,
BTREE_UPDATE_internal_snapshot_node); if (ret) break;
}
}
darray_exit(&whiteouts_done);
bch2_trans_iter_exit(trans, &iter);
}
if (!bch2_snapshot_is_leaf(c, snapshotid)) {
bch_err(c, "need to reconstruct subvol, but have interior node snapshot"); return bch_err_throw(c, fsck_repair_unimplemented);
}
/* * If inum isn't set, that means we're being called from check_dirents, * not check_inodes - the root of this subvolume doesn't exist or we * would have found it there:
*/ if (!inum) { struct btree_iter inode_iter = {}; struct bch_inode_unpacked new_inode;
u64 cpu = raw_smp_processor_id();
/** * key_visible_in_snapshot - returns true if @id is a descendent of @ancestor, * and @ancestor hasn't been overwritten in @seen * * @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
*/ staticbool key_visible_in_snapshot(struct bch_fs *c, struct snapshots_seen *seen,
u32 id, u32 ancestor)
{
EBUG_ON(id > ancestor);
if (id == ancestor) returntrue;
if (!bch2_snapshot_is_ancestor(c, id, ancestor)) returnfalse;
/* * 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:
*/
darray_for_each_reverse(seen->ids, i) if (*i != ancestor && bch2_snapshot_is_ancestor(c, id, *i)) returnfalse;
returntrue;
}
/** * 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. * * @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 * * Assumes we're visiting @src keys in natural key order
*/ staticbool ref_visible(struct bch_fs *c, struct snapshots_seen *s,
u32 src, u32 dst)
{ return dst <= src
? key_visible_in_snapshot(c, s, dst, src)
: bch2_snapshot_is_ancestor(c, src, dst);
}
/* * We no longer have inodes for w->last_pos; clear this to avoid * screwing up check_i_sectors/check_subdir_count if we take a * transaction restart here:
*/
w->have_inodes = false;
w->recalculate_sums = false;
w->inodes.nr = 0;
struct inode_walker_entry *i = darray_find_p(w->inodes, i,
bch2_snapshot_is_ancestor(c, k.k->p.snapshot, i->inode.bi_snapshot));
if (!i) return NULL;
struct printbuf buf = PRINTBUF; int ret = 0;
if (fsck_err_on(k.k->p.snapshot != i->inode.bi_snapshot,
trans, snapshot_key_missing_inode_snapshot, "have key for inode %llu:%u but have inode in ancestor snapshot %u\n" "unexpected because we should always update the inode when we update a key in that inode\n" "%s",
w->last_pos.inode, k.k->p.snapshot, i->inode.bi_snapshot,
(bch2_bkey_val_to_text(&buf, c, k),
buf.buf))) { if (!i->whiteout) { struct bch_inode_unpacked new = i->inode; new.bi_snapshot = k.k->p.snapshot;
ret = __bch2_fsck_write_inode(trans, &new);
} else { struct bkey_i whiteout;
bkey_init(&whiteout.k);
whiteout.k.type = KEY_TYPE_whiteout;
whiteout.k.p = SPOS(0, i->inode.bi_inum, k.k->p.snapshot);
ret = bch2_btree_insert_nonextent(trans, BTREE_ID_inodes,
&whiteout,
BTREE_UPDATE_internal_snapshot_node);
}
if (ret) goto fsck_err;
ret = bch2_trans_commit(trans, NULL, NULL, 0); if (ret) goto fsck_err;
staticstruct inode_walker_entry *walk_inode(struct btree_trans *trans, struct inode_walker *w, struct bkey_s_c k)
{ if (w->last_pos.inode != k.k->p.inode) { int ret = get_inodes_all_snapshots(trans, w, k.k->p.inode); if (ret) return ERR_PTR(ret);
}
w->last_pos = k.k->p;
return lookup_inode_for_snapshot(trans, w, k);
}
/* * Prefer to delete the first one, since that will be the one at the wrong * offset: * return value: 0 -> delete k1, 1 -> delete k2
*/ int bch2_fsck_update_backpointers(struct btree_trans *trans, struct snapshots_seen *s, conststruct bch_hash_desc desc, struct bch_hash_info *hash_info, struct bkey_i *new)
{ if (new->k.type != KEY_TYPE_dirent) return 0;
struct bkey_i_dirent *d = bkey_i_to_dirent(new); struct inode_walker target = inode_walker_init(); int ret = 0;
if (d->v.d_type == DT_SUBVOL) {
bch_err(trans->c, "%s does not support DT_SUBVOL", __func__);
ret = -BCH_ERR_fsck_repair_unimplemented;
} else {
ret = get_visible_inodes(trans, &target, s, le64_to_cpu(d->v.d_inum)); if (ret) goto err;
u32 inode_snapshot = inode->bi_snapshot; struct btree_iter dirent_iter = {}; struct bkey_s_c_dirent d = inode_get_dirent(trans, &dirent_iter, inode, &inode_snapshot); int ret = bkey_err(d); if (ret && !bch2_err_matches(ret, ENOENT)) return ret;
if ((ret || dirent_points_to_inode_nowarn(c, d, inode)) &&
inode->bi_subvol &&
(inode->bi_flags & BCH_INODE_has_child_snapshot)) { /* Older version of a renamed subvolume root: we won't have a * correct dirent for it. That's expected, see * inode_should_reattach(). * * We don't clear the backpointer field when doing the rename * because there might be arbitrarily many versions in older * snapshots.
*/
inode->bi_dir = 0;
inode->bi_dir_offset = 0;
*write_inode = true; goto out;
}
if (fsck_err_on(ret,
trans, inode_points_to_missing_dirent, "inode points to missing dirent\n%s",
(bch2_inode_unpacked_to_text(&buf, inode), buf.buf)) ||
fsck_err_on(!ret && dirent_points_to_inode_nowarn(c, d, inode),
trans, inode_points_to_wrong_dirent, "%s",
(printbuf_reset(&buf),
dirent_inode_mismatch_msg(&buf, c, d, inode),
buf.buf))) { /* * We just clear the backpointer fields for now. If we find a * dirent that points to this inode in check_dirents(), we'll * update it then; then when we get to check_path() if the * backpointer is still 0 we'll reattach it.
*/
inode->bi_dir = 0;
inode->bi_dir_offset = 0;
*write_inode = true;
}
out:
ret = 0;
fsck_err:
bch2_trans_iter_exit(trans, &dirent_iter);
printbuf_exit(&buf);
bch_err_fn(c, ret); return ret;
}
ret = bch2_inode_has_child_snapshots(trans, k.k->p); if (ret < 0) goto err;
if (fsck_err_on(ret != !!(u.bi_flags & BCH_INODE_has_child_snapshot),
trans, inode_has_child_snapshots_wrong, "inode has_child_snapshots flag wrong (should be %u)\n%s",
ret,
(printbuf_reset(&buf),
bch2_inode_unpacked_to_text(&buf, &u),
buf.buf))) { if (ret)
u.bi_flags |= BCH_INODE_has_child_snapshot; else
u.bi_flags &= ~BCH_INODE_has_child_snapshot;
do_update = true;
}
ret = 0;
if ((u.bi_flags & BCH_INODE_unlinked) &&
!(u.bi_flags & BCH_INODE_has_child_snapshot)) { if (!test_bit(BCH_FS_started, &c->flags)) { /* * If we're not in online fsck, don't delete unlinked * inodes, just make sure they're on the deleted list. * * They might be referred to by a logged operation - * i.e. we might have crashed in the middle of a * truncate on an unlinked but open file - so we want to * let the delete_dead_inodes kill it after resuming * logged ops.
*/
ret = check_inode_deleted_list(trans, k.k->p); if (ret < 0) goto err_noprint;
fsck_err_on(!ret,
trans, unlinked_inode_not_on_deleted_list, "inode %llu:%u unlinked, but not on deleted list",
u.bi_inum, k.k->p.snapshot);
ret = bch2_btree_bit_mod_buffered(trans, BTREE_ID_deleted_inodes, k.k->p, 1); if (ret) goto err;
} else {
ret = bch2_inode_or_descendents_is_open(trans, k.k->p); if (ret < 0) goto err;
if (fsck_err_on(!ret,
trans, inode_unlinked_and_not_open, "inode %llu:%u unlinked and not open",
u.bi_inum, u.bi_snapshot)) {
ret = bch2_inode_rm_snapshot(trans, u.bi_inum, iter->pos.snapshot);
bch_err_msg(c, ret, "in fsck deleting inode"); goto err_noprint;
}
ret = 0;
}
}
/* * We look for inodes to reattach in natural key order, leaves first, * but we should do the reattach at the oldest version that needs to be * reattached:
*/
for_each_btree_key_norestart(trans, iter,
BTREE_ID_inodes,
SPOS(0, inode->bi_inum, inode->bi_snapshot + 1),
BTREE_ITER_all_snapshots, k, ret) { if (k.k->p.offset != inode->bi_inum) break;
if (!bch2_snapshot_is_ancestor(c, inode->bi_snapshot, k.k->p.snapshot)) continue;
if (!bkey_is_inode(k.k)) break;
struct bch_inode_unpacked parent_inode;
ret = bch2_inode_unpack(k, &parent_inode); if (ret) break;
staticint check_unreachable_inode(struct btree_trans *trans, struct btree_iter *iter, struct bkey_s_c k)
{ struct printbuf buf = PRINTBUF; int ret = 0;
if (!bkey_is_inode(k.k)) return 0;
struct bch_inode_unpacked inode;
ret = bch2_inode_unpack(k, &inode); if (ret) return ret;
if (!inode_should_reattach(&inode)) return 0;
ret = find_oldest_inode_needs_reattach(trans, &inode); if (ret) return ret;
if (fsck_err(trans, inode_unreachable, "unreachable inode:\n%s",
(bch2_inode_unpacked_to_text(&buf, &inode),
buf.buf)))
ret = reattach_inode(trans, &inode);
fsck_err:
printbuf_exit(&buf); return ret;
}
/* * Reattach unreachable (but not unlinked) inodes * * Run after check_inodes() and check_dirents(), so we node that inode * backpointer fields point to valid dirents, and every inode that has a dirent * that points to it has its backpointer field set - so we're just looking for * non-unlinked inodes without backpointers: * * XXX: this is racy w.r.t. hardlink removal in online fsck
*/ int bch2_check_unreachable_inodes(struct bch_fs *c)
{ 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_unreachable_inode(trans, &iter, k)));
bch_err_fn(c, ret); return ret;
}
staticinlinebool btree_matches_i_mode(enum btree_id btree, unsigned mode)
{ switch (btree) { case BTREE_ID_extents: return S_ISREG(mode) || S_ISLNK(mode); case BTREE_ID_dirents: return S_ISDIR(mode); case BTREE_ID_xattrs: returntrue; default:
BUG();
}
}
if (nr_keys > 100)
prt_printf(&buf, "found > %u keys for this missing inode\n", nr_keys); elseif (nr_keys > 10)
prt_printf(&buf, "found %u keys for this missing inode\n", nr_keys);
if (!have_inode) { if (fsck_err_on(!have_inode,
trans, key_in_missing_inode, "key in missing inode%s", buf.buf)) { /* * Maybe a deletion that raced with data move, or something * weird like that? But if we know the inode was deleted, or * it's just a few keys, we can safely delete them. * * If it's many keys, we should probably recreate the inode
*/ if (have_old_inode || nr_keys <= 2) gotodelete; else goto reconstruct;
}
} else { /* * not autofix, this one would be a giant wtf - bit error in the * inode corrupting i_mode? * * may want to try repairing inode instead of deleting
*/ if (fsck_err_on(!btree_matches_i_mode(iter->btree_id, i->inode.bi_mode),
trans, key_in_wrong_inode_type, "key for wrong inode mode %o%s",
i->inode.bi_mode, buf.buf)) gotodelete;
}
out:
err:
fsck_err:
bch2_trans_iter_exit(trans, &iter2);
printbuf_exit(&buf);
bch_err_fn(c, ret); return ret; delete: /* * XXX: print out more info * count up extents for this inode, check if we have different inode in * an older snapshot version, perhaps decide if we want to reconstitute
*/
ret = bch2_btree_delete_at(trans, iter, BTREE_UPDATE_internal_snapshot_node); goto out;
reconstruct:
ret = reconstruct_inode(trans, iter->btree_id, k.k->p.snapshot, k.k->p.inode) ?:
bch2_trans_commit(trans, NULL, NULL, BCH_TRANS_COMMIT_no_enospc); if (ret) goto err;
inode->last_pos.inode--;
ret = bch_err_throw(c, transaction_restart_nested); goto out;
}
staticint check_i_sectors_notnested(struct btree_trans *trans, struct inode_walker *w)
{ struct bch_fs *c = trans->c; int ret = 0;
s64 count2;
darray_for_each(w->inodes, i) { if (i->inode.bi_sectors == i->count) continue;
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);
} elseif (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_throw(c, transaction_restart_nested);
}
}
fsck_err:
err:
bch2_trans_iter_exit(trans, &iter2);
bch2_trans_iter_exit(trans, &iter1);
printbuf_exit(&buf); return ret;
}
ret = bch2_check_key_has_snapshot(trans, iter, k); if (ret) {
ret = ret < 0 ? ret : 0; goto out;
}
if (inode->last_pos.inode != k.k->p.inode && inode->have_inodes) {
ret = check_i_sectors(trans, inode); if (ret) goto err;
}
ret = snapshots_seen_update(c, s, iter->btree_id, k.k->p); if (ret) goto err;
struct inode_walker_entry *extent_i = walk_inode(trans, inode, k);
ret = PTR_ERR_OR_ZERO(extent_i); if (ret) goto err;
ret = check_key_has_inode(trans, iter, inode, extent_i, k); if (ret) goto err;
if (k.k->type != KEY_TYPE_whiteout) {
ret = check_overlapping_extents(trans, s, extent_ends, k, iter,
&inode->recalculate_sums); if (ret) goto err;
/* * 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:
*/ for (struct inode_walker_entry *i = extent_i ?: &darray_last(inode->inodes);
inode->inodes.data && i >= inode->inodes.data;
--i) { if (i->inode.bi_snapshot > k.k->p.snapshot ||
!key_visible_in_snapshot(c, s, i->inode.bi_snapshot, k.k->p.snapshot)) continue;
if (fsck_err_on(k.k->p.offset > last_block &&
!bkey_extent_is_reservation(k),
trans, extent_past_end_of_inode, "extent type past end of inode %llu:%u, i_size %llu\n%s",
i->inode.bi_inum, i->inode.bi_snapshot, i->inode.bi_size,
(bch2_bkey_val_to_text(&buf, c, k), buf.buf))) {
ret = snapshots_seen_add_inorder(c, s, i->inode.bi_snapshot) ?:
bch2_fpunch_snapshot(trans,
SPOS(i->inode.bi_inum,
last_block,
i->inode.bi_snapshot),
POS(i->inode.bi_inum, U64_MAX)); if (ret) goto err;
iter->k.type = KEY_TYPE_whiteout; break;
}
}
}
ret = bch2_trans_commit(trans, res, NULL, BCH_TRANS_COMMIT_no_enospc); if (ret) goto err;
if (bkey_extent_is_allocation(k.k)) { for (struct inode_walker_entry *i = extent_i ?: &darray_last(inode->inodes);
inode->inodes.data && i >= inode->inodes.data;
--i) { if (i->whiteout ||
i->inode.bi_snapshot > k.k->p.snapshot ||
!key_visible_in_snapshot(c, s, i->inode.bi_snapshot, k.k->p.snapshot)) continue;
i->count += k.k->size;
}
}
if (k.k->type != KEY_TYPE_whiteout) {
ret = extent_ends_at(c, extent_ends, s, k); if (ret) goto err;
}
out:
err:
fsck_err:
printbuf_exit(&buf);
bch_err_fn(c, ret); return ret;
}
/* * Walk extents: verify that extents have a corresponding S_ISREG inode, and * that i_size an i_sectors are consistent
*/ int bch2_check_extents(struct bch_fs *c)
{ struct inode_walker w = inode_walker_init(); struct snapshots_seen s; struct extent_ends extent_ends; struct disk_reservation res = { 0 };
ret = subvol_lookup(trans, parent_subvol, &parent_snapshot, &parent_inum); if (ret && !bch2_err_matches(ret, ENOENT)) return ret;
if (ret ||
(!ret && !bch2_snapshot_is_ancestor(c, parent_snapshot, d.k->p.snapshot))) { int ret2 = find_snapshot_subvol(trans, d.k->p.snapshot, &new_parent_subvol); if (ret2 && !bch2_err_matches(ret, ENOENT)) return ret2;
}
if (ret &&
!new_parent_subvol &&
(c->sb.btrees_lost_data & BIT_ULL(BTREE_ID_subvolumes))) { /* * Couldn't find a subvol for dirent's snapshot - but we lost * subvols, so we need to reconstruct:
*/
ret = reconstruct_subvol(trans, d.k->p.snapshot, parent_subvol, 0); if (ret) return ret;
parent_snapshot = d.k->p.snapshot;
}
if (fsck_err_on(ret,
trans, dirent_to_missing_parent_subvol, "dirent parent_subvol points to missing subvolume\n%s",
(bch2_bkey_val_to_text(&buf, c, d.s_c), buf.buf)) ||
fsck_err_on(!ret && !bch2_snapshot_is_ancestor(c, parent_snapshot, d.k->p.snapshot),
trans, dirent_not_visible_in_parent_subvol, "dirent not visible in parent_subvol (not an ancestor of subvol snap %u)\n%s",
parent_snapshot,
(bch2_bkey_val_to_text(&buf, c, d.s_c), buf.buf))) { if (!new_parent_subvol) {
bch_err(c, "could not find a subvol for snapshot %u", d.k->p.snapshot); return bch_err_throw(c, fsck_repair_unimplemented);
}
struct bkey_i_dirent *new_dirent = bch2_bkey_make_mut_typed(trans, iter, &d.s_c, 0, dirent);
ret = PTR_ERR_OR_ZERO(new_dirent); if (ret) goto err;
ret = bch2_str_hash_check_key(trans, s, &bch2_dirent_hash_desc, hash_info,
iter, k, need_second_pass); if (ret < 0) goto err; if (ret) { /* dirent has been deleted */
ret = 0; goto out;
}
if (k.k->type != KEY_TYPE_dirent) goto out;
struct bkey_s_c_dirent d = bkey_s_c_to_dirent(k);
/* check casefold */ if (fsck_err_on(d.v->d_casefold != !!hash_info->cf_encoding,
trans, dirent_casefold_mismatch, "dirent casefold does not match dir casefold\n%s",
(printbuf_reset(&buf),
bch2_bkey_val_to_text(&buf, c, k),
buf.buf))) {
subvol_inum dir_inum = { .subvol = d.v->d_type == DT_SUBVOL
? le32_to_cpu(d.v->d_parent_subvol)
: 0,
};
u64 target = d.v->d_type == DT_SUBVOL
? le32_to_cpu(d.v->d_child_subvol)
: le64_to_cpu(d.v->d_inum); struct qstr name = bch2_dirent_get_name(d);
struct bkey_i_dirent *new_d =
bch2_dirent_create_key(trans, hash_info, dir_inum,
d.v->d_type, &name, NULL, target);
ret = PTR_ERR_OR_ZERO(new_d); if (ret) goto out;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.