if (in >= end) return -BCH_ERR_inode_unpack_error;
if (!*in) return -BCH_ERR_inode_unpack_error;
/* * position of highest set bit indicates number of bytes: * shift = number of bits to remove in high byte:
*/
shift = 8 - __fls(*in); /* 1 <= shift <= 8 */
bytes = byte_table[shift - 1];
if (in + bytes > end) return -BCH_ERR_inode_unpack_error;
p = (u8 *) be + 16 - bytes;
memcpy(p, in, bytes);
*p ^= (1 << 8) >> shift;
int bch2_inode_find_snapshot_root(struct btree_trans *trans, u64 inum, struct bch_inode_unpacked *root)
{ struct btree_iter iter; struct bkey_s_c k; int ret = 0;
for_each_btree_key_reverse_norestart(trans, iter, BTREE_ID_inodes,
SPOS(0, inum, U32_MAX),
BTREE_ITER_all_snapshots, k, ret) { if (k.k->p.offset != inum) break; if (bkey_is_inode(k.k)) {
ret = bch2_inode_unpack(k, root); goto out;
}
} /* We're only called when we know we have an inode for @inum */
BUG_ON(!ret);
out:
bch2_trans_iter_exit(trans, &iter); return ret;
}
bkey_fsck_err_on((unpacked.bi_flags & BCH_INODE_unlinked) &&
unpacked.bi_nlink != 0,
c, inode_unlinked_but_nlink_nonzero, "flagged as unlinked but bi_nlink != 0");
bkey_fsck_err_on(unpacked.bi_subvol && !S_ISDIR(unpacked.bi_mode),
c, inode_subvol_root_but_not_dir, "subvolume root but not a directory");
fsck_err: return ret;
}
int bch2_inode_validate(struct bch_fs *c, struct bkey_s_c k, struct bkey_validate_context from)
{ struct bkey_s_c_inode inode = bkey_s_c_to_inode(k); int ret = 0;
staticint update_inode_has_children(struct btree_trans *trans, struct bkey_s k, bool have_child)
{ if (!have_child) { int ret = bch2_inode_has_child_snapshots(trans, k.k->p); if (ret) return ret < 0 ? ret : 0;
}
u64 f = bkey_inode_flags(k.s_c); if (have_child != !!(f & BCH_INODE_has_child_snapshot))
bkey_inode_flags_set(k, f ^ BCH_INODE_has_child_snapshot);
return 0;
}
staticint update_parent_inode_has_children(struct btree_trans *trans, struct bpos pos, bool have_child)
{ struct btree_iter iter; struct bkey_s_c k = bch2_inode_get_iter_snapshot_parent(trans,
&iter, pos, BTREE_ITER_with_updates); int ret = bkey_err(k); if (ret) return ret; if (!k.k) return 0;
if (!have_child) {
ret = bch2_inode_has_child_snapshots(trans, k.k->p); if (ret) {
ret = ret < 0 ? ret : 0; goto err;
}
}
u64 f = bkey_inode_flags(k); if (have_child != !!(f & BCH_INODE_has_child_snapshot)) { struct bkey_i *update = bch2_bkey_make_mut(trans, &iter, &k,
BTREE_UPDATE_internal_snapshot_node);
ret = PTR_ERR_OR_ZERO(update); if (ret) goto err;
s64 nr[1] = { bkey_is_inode(new.k) - bkey_is_inode(old.k) }; if ((flags & (BTREE_TRIGGER_transactional|BTREE_TRIGGER_gc)) && nr[0]) { int ret = bch2_disk_accounting_mod2(trans, flags & BTREE_TRIGGER_gc, nr, nr_inodes); if (ret) return ret;
}
if (flags & BTREE_TRIGGER_transactional) { int unlinked_delta = (int) bkey_is_unlinked_inode(new.s_c) -
(int) bkey_is_unlinked_inode(old); if (unlinked_delta) { int ret = bch2_btree_bit_mod_buffered(trans, BTREE_ID_deleted_inodes, new.k->p, unlinked_delta > 0); if (ret) return ret;
}
/* * If we're creating or deleting an inode at this snapshot ID, * and there might be an inode in a parent snapshot ID, we might * need to set or clear the has_child_snapshot flag on the * parent.
*/ int deleted_delta = (int) bkey_is_inode(new.k) -
(int) bkey_is_inode(old.k); if (deleted_delta &&
bch2_snapshot_parent(c, new.k->p.snapshot)) { int ret = update_parent_inode_has_children(trans, new.k->p,
deleted_delta > 0); if (ret) return ret;
}
/* * When an inode is first updated in a new snapshot, we may need * to clear has_child_snapshot
*/ if (deleted_delta > 0) { int ret = update_inode_has_children(trans, new, false); if (ret) return ret;
}
}
return 0;
}
int bch2_inode_generation_validate(struct bch_fs *c, struct bkey_s_c k, struct bkey_validate_context from)
{ int ret = 0;
/* * We don't need to iterate over keys in every snapshot once * we've found just one:
*/
pos = iter->pos.offset + 1;
bch2_btree_iter_set_pos(trans, iter, POS(0, pos));
}
if (!ret && pos < max) goto found_slot;
if (!ret && start == min)
ret = bch_err_throw(trans->c, ENOSPC_inode_create);
if (ret) {
bch2_trans_iter_exit(trans, iter); return ret;
}
/* Retry from start */
pos = start = min;
bch2_btree_iter_set_pos(trans, iter, POS(0, pos));
le32_add_cpu(&cursor->v.gen, 1); goto again;
found_slot:
bch2_btree_iter_set_pos(trans, iter, SPOS(0, pos, snapshot));
k = bch2_btree_iter_peek_slot(trans, iter);
ret = bkey_err(k); if (ret) {
bch2_trans_iter_exit(trans, iter); return ret;
}
/* * We're never going to be deleting partial extents, no need to use an * extent iterator:
*/
bch2_trans_iter_init(trans, &iter, id, POS(inum.inum, 0),
BTREE_ITER_intent);
while (1) {
bch2_trans_begin(trans);
ret = bch2_subvolume_get_snapshot(trans, inum.subvol, &snapshot); if (ret) goto err;
k = bch2_btree_iter_peek_max(trans, &iter, end);
ret = bkey_err(k); if (ret) goto err;
if (!k.k) break;
bkey_init(&delete.k); delete.k.p = iter.pos;
if (iter.flags & BTREE_ITER_is_extents)
bch2_key_resize(&delete.k,
bpos_min(end, k.k->p).offset -
iter.pos.offset);
ret = bch2_trans_update(trans, &iter, &delete, 0) ?:
bch2_trans_commit(trans, NULL, NULL,
BCH_TRANS_COMMIT_no_enospc);
err: if (ret && !bch2_err_matches(ret, BCH_ERR_transaction_restart)) break;
}
bch2_trans_iter_exit(trans, &iter); return ret;
}
int bch2_inode_rm(struct bch_fs *c, subvol_inum inum)
{ struct btree_trans *trans = bch2_trans_get(c); struct btree_iter iter = {}; struct bkey_s_c k;
u32 snapshot; int ret;
ret = lockrestart_do(trans, may_delete_deleted_inum(trans, inum)); if (ret) goto err2;
/* * If this was a directory, there shouldn't be any real dirents left - * but there could be whiteouts (from hash collisions) that we should * delete: * * XXX: the dirent code ideally would delete whiteouts when they're no * longer needed
*/
ret = bch2_inode_delete_keys(trans, inum, BTREE_ID_extents) ?:
bch2_inode_delete_keys(trans, inum, BTREE_ID_xattrs) ?:
bch2_inode_delete_keys(trans, inum, BTREE_ID_dirents); if (ret) goto err2;
retry:
bch2_trans_begin(trans);
ret = bch2_subvolume_get_snapshot(trans, inum.subvol, &snapshot); if (ret) goto err;
k = bch2_bkey_get_iter(trans, &iter, BTREE_ID_inodes,
SPOS(0, inum.inum, snapshot),
BTREE_ITER_intent|BTREE_ITER_cached);
ret = bkey_err(k); if (ret) goto err;
if (!bkey_is_inode(k.k)) {
bch2_fs_inconsistent(c, "inode %llu:%u not found when deleting",
inum.inum, snapshot);
ret = bch_err_throw(c, ENOENT_inode); goto err;
}
ret = bch2_btree_delete_at(trans, &iter, 0) ?:
bch2_trans_commit(trans, NULL, NULL,
BCH_TRANS_COMMIT_no_enospc);
err:
bch2_trans_iter_exit(trans, &iter); if (bch2_err_matches(ret, BCH_ERR_transaction_restart)) goto retry;
#ifndef CONFIG_UNICODE
bch_err(c, "Cannot use casefolding on a kernel without CONFIG_UNICODE"); return -EOPNOTSUPP; #endif
if (c->opts.casefold_disabled) return -EOPNOTSUPP;
int ret = 0; /* Not supported on individual files. */ if (!S_ISDIR(bi->bi_mode)) return -EOPNOTSUPP;
/* * Make sure the dir is empty, as otherwise we'd need to * rehash everything and update the dirent keys.
*/
ret = bch2_empty_dir_trans(trans, inum); if (ret < 0) return ret;
ret = bch2_request_incompat_feature(c, bcachefs_metadata_version_casefolding); if (ret) return ret;
k = bch2_bkey_get_iter(trans, &iter, BTREE_ID_inodes,
SPOS(0, inum, snapshot), BTREE_ITER_intent);
ret = bkey_err(k); if (ret) goto err;
if (!bkey_is_inode(k.k)) {
bch2_fs_inconsistent(c, "inode %llu:%u not found when deleting",
inum, snapshot);
ret = bch_err_throw(c, ENOENT_inode); goto err;
}
bch2_inode_unpack(k, &inode_u);
/* Subvolume root? */ if (inode_u.bi_subvol)
bch_warn(c, "deleting inode %llu marked as unlinked, but also a subvolume root!?", inode_u.bi_inum);
ret = bch2_trans_update(trans, &iter, &delete.k_i, 0) ?:
bch2_trans_commit(trans, NULL, NULL,
BCH_TRANS_COMMIT_no_enospc);
err:
bch2_trans_iter_exit(trans, &iter); if (bch2_err_matches(ret, BCH_ERR_transaction_restart)) goto retry;
return ret ?: -BCH_ERR_transaction_restart_nested;
}
/* * After deleting an inode, there may be versions in older snapshots that should * also be deleted - if they're not referenced by sibling snapshots and not open * in other subvolumes:
*/ staticint delete_ancestor_snapshot_inodes(struct btree_trans *trans, struct bpos pos)
{ struct btree_iter iter; struct bkey_s_c k; int ret;
next_parent:
ret = lockrestart_do(trans,
bkey_err(k = bch2_inode_get_iter_snapshot_parent(trans, &iter, pos, 0))); if (ret || !k.k) return ret;
k = bch2_bkey_get_iter(trans, &inode_iter, BTREE_ID_inodes, pos, BTREE_ITER_cached);
ret = bkey_err(k); if (ret) return ret;
ret = bkey_is_inode(k.k) ? 0 : bch_err_throw(c, ENOENT_inode); if (fsck_err_on(from_deleted_inodes && ret,
trans, deleted_inode_missing, "nonexistent inode %llu:%u in deleted_inodes btree",
pos.offset, pos.snapshot)) gotodelete; if (ret) goto out;
ret = bch2_inode_unpack(k, &inode); if (ret) goto out;
if (S_ISDIR(inode.bi_mode)) {
ret = bch2_empty_dir_snapshot(trans, pos.offset, 0, pos.snapshot); if (fsck_err_on(from_deleted_inodes &&
bch2_err_matches(ret, ENOTEMPTY),
trans, deleted_inode_is_dir, "non empty directory %llu:%u in deleted_inodes btree",
pos.offset, pos.snapshot)) gotodelete; if (ret) goto out;
}
ret = inode.bi_flags & BCH_INODE_unlinked ? 0 : bch_err_throw(c, inode_not_unlinked); if (fsck_err_on(from_deleted_inodes && ret,
trans, deleted_inode_not_unlinked, "non-deleted inode %llu:%u in deleted_inodes btree",
pos.offset, pos.snapshot)) gotodelete; if (ret) goto out;
ret = !(inode.bi_flags & BCH_INODE_has_child_snapshot)
? 0 : bch_err_throw(c, inode_has_child_snapshot);
if (fsck_err_on(from_deleted_inodes && ret,
trans, deleted_inode_has_child_snapshots, "inode with child snapshots %llu:%u in deleted_inodes btree",
pos.offset, pos.snapshot)) gotodelete; if (ret) goto out;
ret = bch2_inode_has_child_snapshots(trans, k.k->p); if (ret < 0) goto out;
if (ret) { if (fsck_err(trans, inode_has_child_snapshots_wrong, "inode has_child_snapshots flag wrong (should be set)\n%s",
(printbuf_reset(&buf),
bch2_inode_unpacked_to_text(&buf, &inode),
buf.buf))) {
inode.bi_flags |= BCH_INODE_has_child_snapshot;
ret = __bch2_fsck_write_inode(trans, &inode); if (ret) goto out;
}
if (!from_deleted_inodes) {
ret = bch2_trans_commit(trans, NULL, NULL, BCH_TRANS_COMMIT_no_enospc) ?:
bch_err_throw(c, inode_has_child_snapshot); goto out;
}
gotodelete;
}
if (from_deleted_inodes) { if (test_bit(BCH_FS_clean_recovery, &c->flags) &&
!fsck_err(trans, deleted_inode_but_clean, "filesystem marked as clean but have deleted inode %llu:%u",
pos.offset, pos.snapshot)) {
ret = 0; goto out;
}
int bch2_delete_dead_inodes(struct bch_fs *c)
{ struct btree_trans *trans = bch2_trans_get(c); int ret;
/* * if we ran check_inodes() unlinked inodes will have already been * cleaned up but the write buffer will be out of sync; therefore we * alway need a write buffer flush
*/
ret = bch2_btree_write_buffer_flush_sync(trans); if (ret) goto err;
/* * Weird transaction restart handling here because on successful delete, * bch2_inode_rm_snapshot() will return a nested transaction restart, * but we can't retry because the btree write buffer won't have been * flushed and we'd spin:
*/
ret = for_each_btree_key_commit(trans, iter, BTREE_ID_deleted_inodes, POS_MIN,
BTREE_ITER_prefetch|BTREE_ITER_all_snapshots, k,
NULL, NULL, BCH_TRANS_COMMIT_no_enospc, ({
ret = may_delete_deleted_inode(trans, k.k->p, true); if (ret > 0) {
bch_verbose_ratelimited(c, "deleting unlinked inode %llu:%u",
k.k->p.offset, k.k->p.snapshot);
ret = bch2_inode_rm_snapshot(trans, k.k->p.offset, k.k->p.snapshot); /* * We don't want to loop here: a transaction restart * error here means we handled a transaction restart and * we're actually done, but if we loop we'll retry the * same key because the write buffer hasn't been flushed * yet
*/ if (bch2_err_matches(ret, BCH_ERR_transaction_restart)) {
ret = 0; continue;
}
}
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.