brintos

brintos / linux-shallow public Read only

0
0
Text · 12.4 KiB · aca2861 Raw
572 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) STRATO AG 2013.  All rights reserved.4 */5 6#include <linux/kthread.h>7#include <linux/uuid.h>8#include <linux/unaligned.h>9#include "messages.h"10#include "ctree.h"11#include "transaction.h"12#include "disk-io.h"13#include "fs.h"14#include "accessors.h"15#include "uuid-tree.h"16#include "ioctl.h"17 18static void btrfs_uuid_to_key(const u8 *uuid, u8 type, struct btrfs_key *key)19{20	key->type = type;21	key->objectid = get_unaligned_le64(uuid);22	key->offset = get_unaligned_le64(uuid + sizeof(u64));23}24 25/* return -ENOENT for !found, < 0 for errors, or 0 if an item was found */26static int btrfs_uuid_tree_lookup(struct btrfs_root *uuid_root, const u8 *uuid,27				  u8 type, u64 subid)28{29	int ret;30	struct btrfs_path *path = NULL;31	struct extent_buffer *eb;32	int slot;33	u32 item_size;34	unsigned long offset;35	struct btrfs_key key;36 37	if (WARN_ON_ONCE(!uuid_root)) {38		ret = -ENOENT;39		goto out;40	}41 42	path = btrfs_alloc_path();43	if (!path) {44		ret = -ENOMEM;45		goto out;46	}47 48	btrfs_uuid_to_key(uuid, type, &key);49	ret = btrfs_search_slot(NULL, uuid_root, &key, path, 0, 0);50	if (ret < 0) {51		goto out;52	} else if (ret > 0) {53		ret = -ENOENT;54		goto out;55	}56 57	eb = path->nodes[0];58	slot = path->slots[0];59	item_size = btrfs_item_size(eb, slot);60	offset = btrfs_item_ptr_offset(eb, slot);61	ret = -ENOENT;62 63	if (!IS_ALIGNED(item_size, sizeof(u64))) {64		btrfs_warn(uuid_root->fs_info,65			   "uuid item with illegal size %lu!",66			   (unsigned long)item_size);67		goto out;68	}69	while (item_size) {70		__le64 data;71 72		read_extent_buffer(eb, &data, offset, sizeof(data));73		if (le64_to_cpu(data) == subid) {74			ret = 0;75			break;76		}77		offset += sizeof(data);78		item_size -= sizeof(data);79	}80 81out:82	btrfs_free_path(path);83	return ret;84}85 86int btrfs_uuid_tree_add(struct btrfs_trans_handle *trans, const u8 *uuid, u8 type,87			u64 subid_cpu)88{89	struct btrfs_fs_info *fs_info = trans->fs_info;90	struct btrfs_root *uuid_root = fs_info->uuid_root;91	int ret;92	struct btrfs_path *path = NULL;93	struct btrfs_key key;94	struct extent_buffer *eb;95	int slot;96	unsigned long offset;97	__le64 subid_le;98 99	ret = btrfs_uuid_tree_lookup(uuid_root, uuid, type, subid_cpu);100	if (ret != -ENOENT)101		return ret;102 103	if (WARN_ON_ONCE(!uuid_root)) {104		ret = -EINVAL;105		goto out;106	}107 108	btrfs_uuid_to_key(uuid, type, &key);109 110	path = btrfs_alloc_path();111	if (!path) {112		ret = -ENOMEM;113		goto out;114	}115 116	ret = btrfs_insert_empty_item(trans, uuid_root, path, &key,117				      sizeof(subid_le));118	if (ret == 0) {119		/* Add an item for the type for the first time */120		eb = path->nodes[0];121		slot = path->slots[0];122		offset = btrfs_item_ptr_offset(eb, slot);123	} else if (ret == -EEXIST) {124		/*125		 * An item with that type already exists.126		 * Extend the item and store the new subid at the end.127		 */128		btrfs_extend_item(trans, path, sizeof(subid_le));129		eb = path->nodes[0];130		slot = path->slots[0];131		offset = btrfs_item_ptr_offset(eb, slot);132		offset += btrfs_item_size(eb, slot) - sizeof(subid_le);133	} else {134		btrfs_warn(fs_info,135			   "insert uuid item failed %d (0x%016llx, 0x%016llx) type %u!",136			   ret, key.objectid, key.offset, type);137		goto out;138	}139 140	ret = 0;141	subid_le = cpu_to_le64(subid_cpu);142	write_extent_buffer(eb, &subid_le, offset, sizeof(subid_le));143	btrfs_mark_buffer_dirty(trans, eb);144 145out:146	btrfs_free_path(path);147	return ret;148}149 150int btrfs_uuid_tree_remove(struct btrfs_trans_handle *trans, const u8 *uuid, u8 type,151			u64 subid)152{153	struct btrfs_fs_info *fs_info = trans->fs_info;154	struct btrfs_root *uuid_root = fs_info->uuid_root;155	int ret;156	struct btrfs_path *path = NULL;157	struct btrfs_key key;158	struct extent_buffer *eb;159	int slot;160	unsigned long offset;161	u32 item_size;162	unsigned long move_dst;163	unsigned long move_src;164	unsigned long move_len;165 166	if (WARN_ON_ONCE(!uuid_root)) {167		ret = -EINVAL;168		goto out;169	}170 171	btrfs_uuid_to_key(uuid, type, &key);172 173	path = btrfs_alloc_path();174	if (!path) {175		ret = -ENOMEM;176		goto out;177	}178 179	ret = btrfs_search_slot(trans, uuid_root, &key, path, -1, 1);180	if (ret < 0) {181		btrfs_warn(fs_info, "error %d while searching for uuid item!",182			   ret);183		goto out;184	}185	if (ret > 0) {186		ret = -ENOENT;187		goto out;188	}189 190	eb = path->nodes[0];191	slot = path->slots[0];192	offset = btrfs_item_ptr_offset(eb, slot);193	item_size = btrfs_item_size(eb, slot);194	if (!IS_ALIGNED(item_size, sizeof(u64))) {195		btrfs_warn(fs_info, "uuid item with illegal size %lu!",196			   (unsigned long)item_size);197		ret = -ENOENT;198		goto out;199	}200	while (item_size) {201		__le64 read_subid;202 203		read_extent_buffer(eb, &read_subid, offset, sizeof(read_subid));204		if (le64_to_cpu(read_subid) == subid)205			break;206		offset += sizeof(read_subid);207		item_size -= sizeof(read_subid);208	}209 210	if (!item_size) {211		ret = -ENOENT;212		goto out;213	}214 215	item_size = btrfs_item_size(eb, slot);216	if (item_size == sizeof(subid)) {217		ret = btrfs_del_item(trans, uuid_root, path);218		goto out;219	}220 221	move_dst = offset;222	move_src = offset + sizeof(subid);223	move_len = item_size - (move_src - btrfs_item_ptr_offset(eb, slot));224	memmove_extent_buffer(eb, move_dst, move_src, move_len);225	btrfs_truncate_item(trans, path, item_size - sizeof(subid), 1);226 227out:228	btrfs_free_path(path);229	return ret;230}231 232static int btrfs_uuid_iter_rem(struct btrfs_root *uuid_root, u8 *uuid, u8 type,233			       u64 subid)234{235	struct btrfs_trans_handle *trans;236	int ret;237 238	/* 1 - for the uuid item */239	trans = btrfs_start_transaction(uuid_root, 1);240	if (IS_ERR(trans)) {241		ret = PTR_ERR(trans);242		goto out;243	}244 245	ret = btrfs_uuid_tree_remove(trans, uuid, type, subid);246	btrfs_end_transaction(trans);247 248out:249	return ret;250}251 252/*253 * Check if there's an matching subvolume for given UUID254 *255 * Return:256 * 0	check succeeded, the entry is not outdated257 * > 0	if the check failed, the caller should remove the entry258 * < 0	if an error occurred259 */260static int btrfs_check_uuid_tree_entry(struct btrfs_fs_info *fs_info,261				       const u8 *uuid, u8 type, u64 subvolid)262{263	int ret = 0;264	struct btrfs_root *subvol_root;265 266	if (type != BTRFS_UUID_KEY_SUBVOL &&267	    type != BTRFS_UUID_KEY_RECEIVED_SUBVOL)268		goto out;269 270	subvol_root = btrfs_get_fs_root(fs_info, subvolid, true);271	if (IS_ERR(subvol_root)) {272		ret = PTR_ERR(subvol_root);273		if (ret == -ENOENT)274			ret = 1;275		goto out;276	}277 278	switch (type) {279	case BTRFS_UUID_KEY_SUBVOL:280		if (memcmp(uuid, subvol_root->root_item.uuid, BTRFS_UUID_SIZE))281			ret = 1;282		break;283	case BTRFS_UUID_KEY_RECEIVED_SUBVOL:284		if (memcmp(uuid, subvol_root->root_item.received_uuid,285			   BTRFS_UUID_SIZE))286			ret = 1;287		break;288	}289	btrfs_put_root(subvol_root);290out:291	return ret;292}293 294int btrfs_uuid_tree_iterate(struct btrfs_fs_info *fs_info)295{296	struct btrfs_root *root = fs_info->uuid_root;297	struct btrfs_key key;298	struct btrfs_path *path;299	int ret = 0;300	struct extent_buffer *leaf;301	int slot;302	u32 item_size;303	unsigned long offset;304 305	path = btrfs_alloc_path();306	if (!path) {307		ret = -ENOMEM;308		goto out;309	}310 311	key.objectid = 0;312	key.type = 0;313	key.offset = 0;314 315again_search_slot:316	ret = btrfs_search_forward(root, &key, path, BTRFS_OLDEST_GENERATION);317	if (ret) {318		if (ret > 0)319			ret = 0;320		goto out;321	}322 323	while (1) {324		if (btrfs_fs_closing(fs_info)) {325			ret = -EINTR;326			goto out;327		}328		cond_resched();329		leaf = path->nodes[0];330		slot = path->slots[0];331		btrfs_item_key_to_cpu(leaf, &key, slot);332 333		if (key.type != BTRFS_UUID_KEY_SUBVOL &&334		    key.type != BTRFS_UUID_KEY_RECEIVED_SUBVOL)335			goto skip;336 337		offset = btrfs_item_ptr_offset(leaf, slot);338		item_size = btrfs_item_size(leaf, slot);339		if (!IS_ALIGNED(item_size, sizeof(u64))) {340			btrfs_warn(fs_info,341				   "uuid item with illegal size %lu!",342				   (unsigned long)item_size);343			goto skip;344		}345		while (item_size) {346			u8 uuid[BTRFS_UUID_SIZE];347			__le64 subid_le;348			u64 subid_cpu;349 350			put_unaligned_le64(key.objectid, uuid);351			put_unaligned_le64(key.offset, uuid + sizeof(u64));352			read_extent_buffer(leaf, &subid_le, offset,353					   sizeof(subid_le));354			subid_cpu = le64_to_cpu(subid_le);355			ret = btrfs_check_uuid_tree_entry(fs_info, uuid,356							  key.type, subid_cpu);357			if (ret < 0)358				goto out;359			if (ret > 0) {360				btrfs_release_path(path);361				ret = btrfs_uuid_iter_rem(root, uuid, key.type,362							  subid_cpu);363				if (ret == 0) {364					/*365					 * this might look inefficient, but the366					 * justification is that it is an367					 * exception that check_func returns 1,368					 * and that in the regular case only one369					 * entry per UUID exists.370					 */371					goto again_search_slot;372				}373				if (ret < 0 && ret != -ENOENT)374					goto out;375				key.offset++;376				goto again_search_slot;377			}378			item_size -= sizeof(subid_le);379			offset += sizeof(subid_le);380		}381 382skip:383		ret = btrfs_next_item(root, path);384		if (ret == 0)385			continue;386		else if (ret > 0)387			ret = 0;388		break;389	}390 391out:392	btrfs_free_path(path);393	return ret;394}395 396int btrfs_uuid_scan_kthread(void *data)397{398	struct btrfs_fs_info *fs_info = data;399	struct btrfs_root *root = fs_info->tree_root;400	struct btrfs_key key;401	struct btrfs_path *path = NULL;402	int ret = 0;403	struct extent_buffer *eb;404	int slot;405	struct btrfs_root_item root_item;406	u32 item_size;407	struct btrfs_trans_handle *trans = NULL;408	bool closing = false;409 410	path = btrfs_alloc_path();411	if (!path) {412		ret = -ENOMEM;413		goto out;414	}415 416	key.objectid = 0;417	key.type = BTRFS_ROOT_ITEM_KEY;418	key.offset = 0;419 420	while (1) {421		if (btrfs_fs_closing(fs_info)) {422			closing = true;423			break;424		}425		ret = btrfs_search_forward(root, &key, path,426				BTRFS_OLDEST_GENERATION);427		if (ret) {428			if (ret > 0)429				ret = 0;430			break;431		}432 433		if (key.type != BTRFS_ROOT_ITEM_KEY ||434		    (key.objectid < BTRFS_FIRST_FREE_OBJECTID &&435		     key.objectid != BTRFS_FS_TREE_OBJECTID) ||436		    key.objectid > BTRFS_LAST_FREE_OBJECTID)437			goto skip;438 439		eb = path->nodes[0];440		slot = path->slots[0];441		item_size = btrfs_item_size(eb, slot);442		if (item_size < sizeof(root_item))443			goto skip;444 445		read_extent_buffer(eb, &root_item,446				   btrfs_item_ptr_offset(eb, slot),447				   (int)sizeof(root_item));448		if (btrfs_root_refs(&root_item) == 0)449			goto skip;450 451		if (!btrfs_is_empty_uuid(root_item.uuid) ||452		    !btrfs_is_empty_uuid(root_item.received_uuid)) {453			if (trans)454				goto update_tree;455 456			btrfs_release_path(path);457			/*458			 * 1 - subvol uuid item459			 * 1 - received_subvol uuid item460			 */461			trans = btrfs_start_transaction(fs_info->uuid_root, 2);462			if (IS_ERR(trans)) {463				ret = PTR_ERR(trans);464				break;465			}466			continue;467		} else {468			goto skip;469		}470update_tree:471		btrfs_release_path(path);472		if (!btrfs_is_empty_uuid(root_item.uuid)) {473			ret = btrfs_uuid_tree_add(trans, root_item.uuid,474						  BTRFS_UUID_KEY_SUBVOL,475						  key.objectid);476			if (ret < 0) {477				btrfs_warn(fs_info, "uuid_tree_add failed %d",478					ret);479				break;480			}481		}482 483		if (!btrfs_is_empty_uuid(root_item.received_uuid)) {484			ret = btrfs_uuid_tree_add(trans,485						  root_item.received_uuid,486						 BTRFS_UUID_KEY_RECEIVED_SUBVOL,487						  key.objectid);488			if (ret < 0) {489				btrfs_warn(fs_info, "uuid_tree_add failed %d",490					ret);491				break;492			}493		}494 495skip:496		btrfs_release_path(path);497		if (trans) {498			ret = btrfs_end_transaction(trans);499			trans = NULL;500			if (ret)501				break;502		}503 504		if (key.offset < (u64)-1) {505			key.offset++;506		} else if (key.type < BTRFS_ROOT_ITEM_KEY) {507			key.offset = 0;508			key.type = BTRFS_ROOT_ITEM_KEY;509		} else if (key.objectid < (u64)-1) {510			key.offset = 0;511			key.type = BTRFS_ROOT_ITEM_KEY;512			key.objectid++;513		} else {514			break;515		}516		cond_resched();517	}518 519out:520	btrfs_free_path(path);521	if (trans && !IS_ERR(trans))522		btrfs_end_transaction(trans);523	if (ret)524		btrfs_warn(fs_info, "btrfs_uuid_scan_kthread failed %d", ret);525	else if (!closing)526		set_bit(BTRFS_FS_UPDATE_UUID_TREE_GEN, &fs_info->flags);527	up(&fs_info->uuid_tree_rescan_sem);528	return 0;529}530 531int btrfs_create_uuid_tree(struct btrfs_fs_info *fs_info)532{533	struct btrfs_trans_handle *trans;534	struct btrfs_root *tree_root = fs_info->tree_root;535	struct btrfs_root *uuid_root;536	struct task_struct *task;537	int ret;538 539	/*540	 * 1 - root node541	 * 1 - root item542	 */543	trans = btrfs_start_transaction(tree_root, 2);544	if (IS_ERR(trans))545		return PTR_ERR(trans);546 547	uuid_root = btrfs_create_tree(trans, BTRFS_UUID_TREE_OBJECTID);548	if (IS_ERR(uuid_root)) {549		ret = PTR_ERR(uuid_root);550		btrfs_abort_transaction(trans, ret);551		btrfs_end_transaction(trans);552		return ret;553	}554 555	fs_info->uuid_root = uuid_root;556 557	ret = btrfs_commit_transaction(trans);558	if (ret)559		return ret;560 561	down(&fs_info->uuid_tree_rescan_sem);562	task = kthread_run(btrfs_uuid_scan_kthread, fs_info, "btrfs-uuid");563	if (IS_ERR(task)) {564		/* fs_info->update_uuid_tree_gen remains 0 in all error case */565		btrfs_warn(fs_info, "failed to start uuid_scan task");566		up(&fs_info->uuid_tree_rescan_sem);567		return PTR_ERR(task);568	}569 570	return 0;571}572