235 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			235 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			C
		
	
	
	
// SPDX-License-Identifier: GPL-2.0+
 | 
						|
/*
 | 
						|
 * BTRFS filesystem implementation for U-Boot
 | 
						|
 *
 | 
						|
 * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz
 | 
						|
 */
 | 
						|
 | 
						|
#include <malloc.h>
 | 
						|
#include "ctree.h"
 | 
						|
#include "btrfs.h"
 | 
						|
#include "disk-io.h"
 | 
						|
 | 
						|
/*
 | 
						|
 * Resolve the path of ino inside subvolume @root into @path_ret.
 | 
						|
 *
 | 
						|
 * @path_ret must be at least PATH_MAX size.
 | 
						|
 */
 | 
						|
static int get_path_in_subvol(struct btrfs_root *root, u64 ino, char *path_ret)
 | 
						|
{
 | 
						|
	struct btrfs_path path;
 | 
						|
	struct btrfs_key key;
 | 
						|
	char *tmp;
 | 
						|
	u64 cur = ino;
 | 
						|
	int ret = 0;
 | 
						|
 | 
						|
	tmp = malloc(PATH_MAX);
 | 
						|
	if (!tmp)
 | 
						|
		return -ENOMEM;
 | 
						|
	tmp[0] = '\0';
 | 
						|
 | 
						|
	btrfs_init_path(&path);
 | 
						|
	while (cur != BTRFS_FIRST_FREE_OBJECTID) {
 | 
						|
		struct btrfs_inode_ref *iref;
 | 
						|
		int name_len;
 | 
						|
 | 
						|
		btrfs_release_path(&path);
 | 
						|
		key.objectid = cur;
 | 
						|
		key.type = BTRFS_INODE_REF_KEY;
 | 
						|
		key.offset = (u64)-1;
 | 
						|
 | 
						|
		ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
 | 
						|
		/* Impossible */
 | 
						|
		if (ret == 0)
 | 
						|
			ret = -EUCLEAN;
 | 
						|
		if (ret < 0)
 | 
						|
			goto out;
 | 
						|
		ret = btrfs_previous_item(root, &path, cur,
 | 
						|
					  BTRFS_INODE_REF_KEY);
 | 
						|
		if (ret > 0)
 | 
						|
			ret = -ENOENT;
 | 
						|
		if (ret < 0)
 | 
						|
			goto out;
 | 
						|
 | 
						|
		strncpy(tmp, path_ret, PATH_MAX);
 | 
						|
		iref = btrfs_item_ptr(path.nodes[0], path.slots[0],
 | 
						|
				      struct btrfs_inode_ref);
 | 
						|
		name_len = btrfs_inode_ref_name_len(path.nodes[0],
 | 
						|
						    iref);
 | 
						|
		if (name_len > BTRFS_NAME_LEN) {
 | 
						|
			ret = -ENAMETOOLONG;
 | 
						|
			goto out;
 | 
						|
		}
 | 
						|
		read_extent_buffer(path.nodes[0], path_ret,
 | 
						|
				   (unsigned long)(iref + 1), name_len);
 | 
						|
		path_ret[name_len] = '/';
 | 
						|
		path_ret[name_len + 1] = '\0';
 | 
						|
		strncat(path_ret, tmp, PATH_MAX);
 | 
						|
 | 
						|
		btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
 | 
						|
		cur = key.offset;
 | 
						|
	}
 | 
						|
out:
 | 
						|
	btrfs_release_path(&path);
 | 
						|
	free(tmp);
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
static int list_one_subvol(struct btrfs_root *root, char *path_ret)
 | 
						|
{
 | 
						|
	struct btrfs_fs_info *fs_info = root->fs_info;
 | 
						|
	struct btrfs_root *tree_root = fs_info->tree_root;
 | 
						|
	struct btrfs_path path;
 | 
						|
	struct btrfs_key key;
 | 
						|
	char *tmp;
 | 
						|
	u64 cur = root->root_key.objectid;
 | 
						|
	int ret = 0;
 | 
						|
 | 
						|
	tmp = malloc(PATH_MAX);
 | 
						|
	if (!tmp)
 | 
						|
		return -ENOMEM;
 | 
						|
	tmp[0] = '\0';
 | 
						|
	path_ret[0] = '\0';
 | 
						|
	btrfs_init_path(&path);
 | 
						|
	while (cur != BTRFS_FS_TREE_OBJECTID) {
 | 
						|
		struct btrfs_root_ref *rr;
 | 
						|
		struct btrfs_key location;
 | 
						|
		int name_len;
 | 
						|
		u64 ino;
 | 
						|
 | 
						|
		key.objectid = cur;
 | 
						|
		key.type = BTRFS_ROOT_BACKREF_KEY;
 | 
						|
		key.offset = (u64)-1;
 | 
						|
		btrfs_release_path(&path);
 | 
						|
 | 
						|
		ret = btrfs_search_slot(NULL, tree_root, &key, &path, 0, 0);
 | 
						|
		if (ret == 0)
 | 
						|
			ret = -EUCLEAN;
 | 
						|
		if (ret < 0)
 | 
						|
			goto out;
 | 
						|
		ret = btrfs_previous_item(tree_root, &path, cur,
 | 
						|
					  BTRFS_ROOT_BACKREF_KEY);
 | 
						|
		if (ret > 0)
 | 
						|
			ret = -ENOENT;
 | 
						|
		if (ret < 0)
 | 
						|
			goto out;
 | 
						|
 | 
						|
		/* Get the subvolume name */
 | 
						|
		rr = btrfs_item_ptr(path.nodes[0], path.slots[0],
 | 
						|
				    struct btrfs_root_ref);
 | 
						|
		strncpy(tmp, path_ret, PATH_MAX);
 | 
						|
		name_len = btrfs_root_ref_name_len(path.nodes[0], rr);
 | 
						|
		if (name_len > BTRFS_NAME_LEN) {
 | 
						|
			ret = -ENAMETOOLONG;
 | 
						|
			goto out;
 | 
						|
		}
 | 
						|
		ino = btrfs_root_ref_dirid(path.nodes[0], rr);
 | 
						|
		read_extent_buffer(path.nodes[0], path_ret,
 | 
						|
				   (unsigned long)(rr + 1), name_len);
 | 
						|
		path_ret[name_len] = '/';
 | 
						|
		path_ret[name_len + 1] = '\0';
 | 
						|
		strncat(path_ret, tmp, PATH_MAX);
 | 
						|
 | 
						|
		/* Get the path inside the parent subvolume */
 | 
						|
		btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
 | 
						|
		location.objectid = key.offset;
 | 
						|
		location.type = BTRFS_ROOT_ITEM_KEY;
 | 
						|
		location.offset = (u64)-1;
 | 
						|
		root = btrfs_read_fs_root(fs_info, &location);
 | 
						|
		if (IS_ERR(root)) {
 | 
						|
			ret = PTR_ERR(root);
 | 
						|
			goto out;
 | 
						|
		}
 | 
						|
		ret = get_path_in_subvol(root, ino, path_ret);
 | 
						|
		if (ret < 0)
 | 
						|
			goto out;
 | 
						|
		cur = key.offset;
 | 
						|
	}
 | 
						|
	/* Add the leading '/' */
 | 
						|
	strncpy(tmp, path_ret, PATH_MAX);
 | 
						|
	strncpy(path_ret, "/", PATH_MAX);
 | 
						|
	strncat(path_ret, tmp, PATH_MAX);
 | 
						|
out:
 | 
						|
	btrfs_release_path(&path);
 | 
						|
	free(tmp);
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
static int list_subvolums(struct btrfs_fs_info *fs_info)
 | 
						|
{
 | 
						|
	struct btrfs_root *tree_root = fs_info->tree_root;
 | 
						|
	struct btrfs_root *root;
 | 
						|
	struct btrfs_path path;
 | 
						|
	struct btrfs_key key;
 | 
						|
	char *result;
 | 
						|
	int ret = 0;
 | 
						|
 | 
						|
	result = malloc(PATH_MAX);
 | 
						|
	if (!result)
 | 
						|
		return -ENOMEM;
 | 
						|
 | 
						|
	ret = list_one_subvol(fs_info->fs_root, result);
 | 
						|
	if (ret < 0)
 | 
						|
		goto out;
 | 
						|
	root = fs_info->fs_root;
 | 
						|
	printf("ID %llu gen %llu path %.*s\n",
 | 
						|
		root->root_key.objectid, btrfs_root_generation(&root->root_item),
 | 
						|
		PATH_MAX, result);
 | 
						|
 | 
						|
	key.objectid = BTRFS_FIRST_FREE_OBJECTID;
 | 
						|
	key.type = BTRFS_ROOT_ITEM_KEY;
 | 
						|
	key.offset = 0;
 | 
						|
	btrfs_init_path(&path);
 | 
						|
	ret = btrfs_search_slot(NULL, tree_root, &key, &path, 0, 0);
 | 
						|
	if (ret < 0)
 | 
						|
		goto out;
 | 
						|
	while (1) {
 | 
						|
		if (path.slots[0] >= btrfs_header_nritems(path.nodes[0]))
 | 
						|
			goto next;
 | 
						|
 | 
						|
		btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
 | 
						|
		if (key.objectid > BTRFS_LAST_FREE_OBJECTID)
 | 
						|
			break;
 | 
						|
		if (key.objectid < BTRFS_FIRST_FREE_OBJECTID ||
 | 
						|
		    key.type != BTRFS_ROOT_ITEM_KEY)
 | 
						|
			goto next;
 | 
						|
		key.offset = (u64)-1;
 | 
						|
		root = btrfs_read_fs_root(fs_info, &key);
 | 
						|
		if (IS_ERR(root)) {
 | 
						|
			ret = PTR_ERR(root);
 | 
						|
			if (ret == -ENOENT)
 | 
						|
				goto next;
 | 
						|
		}
 | 
						|
		ret = list_one_subvol(root, result);
 | 
						|
		if (ret < 0)
 | 
						|
			goto out;
 | 
						|
		printf("ID %llu gen %llu path %.*s\n",
 | 
						|
			root->root_key.objectid,
 | 
						|
			btrfs_root_generation(&root->root_item),
 | 
						|
			PATH_MAX, result);
 | 
						|
next:
 | 
						|
		ret = btrfs_next_item(tree_root, &path);
 | 
						|
		if (ret < 0)
 | 
						|
			goto out;
 | 
						|
		if (ret > 0) {
 | 
						|
			ret = 0;
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
out:
 | 
						|
	free(result);
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
void btrfs_list_subvols(void)
 | 
						|
{
 | 
						|
	struct btrfs_fs_info *fs_info = current_fs_info;
 | 
						|
	int ret;
 | 
						|
 | 
						|
	if (!fs_info)
 | 
						|
		return;
 | 
						|
	ret = list_subvolums(fs_info);
 | 
						|
	if (ret < 0)
 | 
						|
		error("failed to list subvolume: %d", ret);
 | 
						|
}
 |