Commit 7f76b08a authored by Kent Overstreet's avatar Kent Overstreet
Browse files

bcachefs: Subvolumes may now be renamed



Files within a subvolume cannot be renamed into another subvolume, but
subvolumes themselves were intended to be.

This implements subvolume renaming - we need to ensure that there's only
a single dirent that points to a subvolume key (not multiple versions in
different snapshots), and we need to ensure that dirent.d_parent_subol
and inode.bi_parent_subvol are updated.

Signed-off-by: default avatarKent Overstreet <kent.overstreet@linux.dev>
parent 5f43b013
Loading
Loading
Loading
Loading
+40 −26
Original line number Diff line number Diff line
@@ -293,12 +293,10 @@ int bch2_dirent_rename(struct btree_trans *trans,
	struct bkey_i_dirent *new_src = NULL, *new_dst = NULL;
	struct bpos dst_pos =
		POS(dst_dir.inum, bch2_dirent_hash(dst_hash, dst_name));
	unsigned src_type = 0, dst_type = 0, src_update_flags = 0;
	unsigned src_update_flags = 0;
	bool delete_src, delete_dst;
	int ret = 0;

	if (src_dir.subvol != dst_dir.subvol)
		return -EXDEV;

	memset(src_inum, 0, sizeof(*src_inum));
	memset(dst_inum, 0, sizeof(*dst_inum));

@@ -319,12 +317,6 @@ int bch2_dirent_rename(struct btree_trans *trans,
	if (ret)
		goto out;

	src_type = bkey_s_c_to_dirent(old_src).v->d_type;

	if (src_type == DT_SUBVOL && mode == BCH_RENAME_EXCHANGE)
		return -EOPNOTSUPP;


	/* Lookup dst: */
	if (mode == BCH_RENAME) {
		/*
@@ -352,11 +344,6 @@ int bch2_dirent_rename(struct btree_trans *trans,
				bkey_s_c_to_dirent(old_dst), dst_inum);
		if (ret)
			goto out;

		dst_type = bkey_s_c_to_dirent(old_dst).v->d_type;

		if (dst_type == DT_SUBVOL)
			return -EOPNOTSUPP;
	}

	if (mode != BCH_RENAME_EXCHANGE)
@@ -426,28 +413,55 @@ int bch2_dirent_rename(struct btree_trans *trans,
		}
	}

	if (new_dst->v.d_type == DT_SUBVOL)
		new_dst->v.d_parent_subvol = cpu_to_le32(dst_dir.subvol);

	if ((mode == BCH_RENAME_EXCHANGE) &&
	    new_src->v.d_type == DT_SUBVOL)
		new_src->v.d_parent_subvol = cpu_to_le32(src_dir.subvol);

	ret = bch2_trans_update(trans, &dst_iter, &new_dst->k_i, 0);
	if (ret)
		goto out;
out_set_src:

	/*
	 * If we're deleting a subvolume, we need to really delete the dirent,
	 * not just emit a whiteout in the current snapshot:
	 * If we're deleting a subvolume we need to really delete the dirent,
	 * not just emit a whiteout in the current snapshot - there can only be
	 * single dirent that points to a given subvolume.
	 *
	 * IOW, we don't maintain multiple versions in different snapshots of
	 * dirents that point to subvolumes - dirents that point to subvolumes
	 * are only visible in one particular subvolume so it's not necessary,
	 * and it would be particularly confusing for fsck to have to deal with.
	 */
	if (src_type == DT_SUBVOL) {
		bch2_btree_iter_set_snapshot(&src_iter, old_src.k->p.snapshot);
		ret = bch2_btree_iter_traverse(&src_iter);
	delete_src = bkey_s_c_to_dirent(old_src).v->d_type == DT_SUBVOL &&
		new_src->k.p.snapshot != old_src.k->p.snapshot;

	delete_dst = old_dst.k &&
		bkey_s_c_to_dirent(old_dst).v->d_type == DT_SUBVOL &&
		new_dst->k.p.snapshot != old_dst.k->p.snapshot;

	if (!delete_src || !bkey_deleted(&new_src->k)) {
		ret = bch2_trans_update(trans, &src_iter, &new_src->k_i, src_update_flags);
		if (ret)
			goto out;
	}

		new_src->k.p = src_iter.pos;
		src_update_flags |= BTREE_UPDATE_INTERNAL_SNAPSHOT_NODE;
	if (delete_src) {
		bch2_btree_iter_set_snapshot(&src_iter, old_src.k->p.snapshot);
		ret =   bch2_btree_iter_traverse(&src_iter) ?:
			bch2_btree_delete_at(trans, &src_iter, BTREE_UPDATE_INTERNAL_SNAPSHOT_NODE);
		if (ret)
			goto out;
	}

	ret = bch2_trans_update(trans, &src_iter, &new_src->k_i, src_update_flags);
	if (delete_dst) {
		bch2_btree_iter_set_snapshot(&dst_iter, old_dst.k->p.snapshot);
		ret =   bch2_btree_iter_traverse(&dst_iter) ?:
			bch2_btree_delete_at(trans, &dst_iter, BTREE_UPDATE_INTERNAL_SNAPSHOT_NODE);
		if (ret)
			goto out;
	}

	if (mode == BCH_RENAME_EXCHANGE)
		*src_offset = new_src->k.p.offset;
+15 −0
Original line number Diff line number Diff line
@@ -410,6 +410,21 @@ int bch2_rename_trans(struct btree_trans *trans,
			goto err;
	}

	/* Can't move across subvolumes, unless it's a subvolume root: */
	if (src_dir.subvol != dst_dir.subvol &&
	    (!src_inode_u->bi_subvol ||
	     (dst_inum.inum && !dst_inode_u->bi_subvol))) {
		ret = -EXDEV;
		goto err;
	}

	if (src_inode_u->bi_parent_subvol)
		src_inode_u->bi_parent_subvol = dst_dir.subvol;

	if ((mode == BCH_RENAME_EXCHANGE) &&
	    dst_inode_u->bi_parent_subvol)
		dst_inode_u->bi_parent_subvol = src_dir.subvol;

	src_inode_u->bi_dir		= dst_dir_u->bi_inum;
	src_inode_u->bi_dir_offset	= dst_offset;