mirror of
				https://source.denx.de/u-boot/u-boot.git
				synced 2025-10-31 16:31:25 +01:00 
			
		
		
		
	Fix diacritics in some instances of my name and change my e-mail address to kabel@kernel.org. Add corresponding .mailmap entries. Signed-off-by: Marek Behún <kabel@kernel.org> Reviewed-by: Stefan Roese <sr@denx.de>
		
			
				
	
	
		
			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 Behún, CZ.NIC, kabel@kernel.org
 | |
|  */
 | |
| 
 | |
| #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);
 | |
| }
 |