mirror of
				https://github.com/smaeul/u-boot.git
				synced 2025-10-31 03:58:17 +00:00 
			
		
		
		
	Move this header to include/u-boot/ so that it can be used by external tools. Reviewed-by: Ilias Apalodimas <ilias.apalodimas@linaro.org> Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>
		
			
				
	
	
		
			286 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			286 lines
		
	
	
		
			6.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 <config.h>
 | |
| #include <malloc.h>
 | |
| #include <u-boot/uuid.h>
 | |
| #include <linux/time.h>
 | |
| #include "btrfs.h"
 | |
| #include "crypto/hash.h"
 | |
| #include "disk-io.h"
 | |
| 
 | |
| struct btrfs_fs_info *current_fs_info;
 | |
| 
 | |
| static int show_dir(struct btrfs_root *root, struct extent_buffer *eb,
 | |
| 		    struct btrfs_dir_item *di)
 | |
| {
 | |
| 	struct btrfs_fs_info *fs_info = root->fs_info;
 | |
| 	struct btrfs_inode_item ii;
 | |
| 	struct btrfs_key key;
 | |
| 	static const char* dir_item_str[] = {
 | |
| 		[BTRFS_FT_REG_FILE]	= "   ",
 | |
| 		[BTRFS_FT_DIR]		= "DIR",
 | |
| 		[BTRFS_FT_CHRDEV]	= "CHR",
 | |
| 		[BTRFS_FT_BLKDEV]	= "BLK",
 | |
| 		[BTRFS_FT_FIFO]		= "FIF",
 | |
| 		[BTRFS_FT_SOCK]		= "SCK",
 | |
| 		[BTRFS_FT_SYMLINK]	= "SYM",
 | |
| 	};
 | |
| 	u8 type = btrfs_dir_type(eb, di);
 | |
| 	char namebuf[BTRFS_NAME_LEN];
 | |
| 	char *target = NULL;
 | |
| 	char filetime[32];
 | |
| 	time_t mtime;
 | |
| 	int ret = 0;
 | |
| 
 | |
| 	/* skip XATTRs in directory listing */
 | |
| 	if (type == BTRFS_FT_XATTR)
 | |
| 		return 0;
 | |
| 
 | |
| 	btrfs_dir_item_key_to_cpu(eb, di, &key);
 | |
| 
 | |
| 	if (key.type == BTRFS_ROOT_ITEM_KEY) {
 | |
| 		struct btrfs_root *subvol;
 | |
| 
 | |
| 		/* It's a subvolume, get its mtime from root item */
 | |
| 		subvol = btrfs_read_fs_root(fs_info, &key);
 | |
| 		if (IS_ERR(subvol)) {
 | |
| 			ret = PTR_ERR(subvol);
 | |
| 			error("Can't find root %llu", key.objectid);
 | |
| 			return ret;
 | |
| 		}
 | |
| 		mtime = btrfs_stack_timespec_sec(&subvol->root_item.otime);
 | |
| 	} else {
 | |
| 		struct btrfs_path path;
 | |
| 
 | |
| 		/* It's regular inode, get its mtime from inode item */
 | |
| 		btrfs_init_path(&path);
 | |
| 		ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
 | |
| 		if (ret > 0)
 | |
| 			ret = -ENOENT;
 | |
| 		if (ret < 0) {
 | |
| 			error("Can't find inode %llu", key.objectid);
 | |
| 			btrfs_release_path(&path);
 | |
| 			return ret;
 | |
| 		}
 | |
| 		read_extent_buffer(path.nodes[0], &ii,
 | |
| 			btrfs_item_ptr_offset(path.nodes[0], path.slots[0]),
 | |
| 			sizeof(ii));
 | |
| 		btrfs_release_path(&path);
 | |
| 		mtime = btrfs_stack_timespec_sec(&ii.mtime);
 | |
| 	}
 | |
| 	ctime_r(&mtime, filetime);
 | |
| 
 | |
| 	if (type == BTRFS_FT_SYMLINK) {
 | |
| 		target = malloc(fs_info->sectorsize);
 | |
| 		if (!target) {
 | |
| 			error("Can't alloc memory for symlink %llu",
 | |
| 				key.objectid);
 | |
| 			return -ENOMEM;
 | |
| 		}
 | |
| 		ret = btrfs_readlink(root, key.objectid, target);
 | |
| 		if (ret < 0) {
 | |
| 			error("Failed to read symlink %llu", key.objectid);
 | |
| 			goto out;
 | |
| 		}
 | |
| 		target[ret] = '\0';
 | |
| 	}
 | |
| 
 | |
| 	if (type < ARRAY_SIZE(dir_item_str) && dir_item_str[type])
 | |
| 		printf("<%s> ", dir_item_str[type]);
 | |
| 	else
 | |
| 		printf("?%3u? ", type);
 | |
| 	if (type == BTRFS_FT_CHRDEV || type == BTRFS_FT_BLKDEV) {
 | |
| 		ASSERT(key.type == BTRFS_INODE_ITEM_KEY);
 | |
| 		printf("%4llu,%5llu  ", btrfs_stack_inode_rdev(&ii) >> 20,
 | |
| 				btrfs_stack_inode_rdev(&ii) & 0xfffff);
 | |
| 	} else {
 | |
| 		if (key.type == BTRFS_INODE_ITEM_KEY)
 | |
| 			printf("%10llu  ", btrfs_stack_inode_size(&ii));
 | |
| 		else
 | |
| 			printf("%10llu  ", 0ULL);
 | |
| 	}
 | |
| 
 | |
| 	read_extent_buffer(eb, namebuf, (unsigned long)(di + 1),
 | |
| 			   btrfs_dir_name_len(eb, di));
 | |
| 	printf("%24.24s  %.*s", filetime, btrfs_dir_name_len(eb, di), namebuf);
 | |
| 	if (type == BTRFS_FT_SYMLINK)
 | |
| 		printf(" -> %s", target ? target : "?");
 | |
| 	printf("\n");
 | |
| out:
 | |
| 	free(target);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| int btrfs_probe(struct blk_desc *fs_dev_desc,
 | |
| 		struct disk_partition *fs_partition)
 | |
| {
 | |
| 	struct btrfs_fs_info *fs_info;
 | |
| 	int ret = -1;
 | |
| 
 | |
| 	btrfs_hash_init();
 | |
| 	fs_info = open_ctree_fs_info(fs_dev_desc, fs_partition);
 | |
| 	if (fs_info) {
 | |
| 		current_fs_info = fs_info;
 | |
| 		ret = 0;
 | |
| 	}
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| int btrfs_ls(const char *path)
 | |
| {
 | |
| 	struct btrfs_fs_info *fs_info = current_fs_info;
 | |
| 	struct btrfs_root *root = fs_info->fs_root;
 | |
| 	u64 ino = BTRFS_FIRST_FREE_OBJECTID;
 | |
| 	u8 type;
 | |
| 	int ret;
 | |
| 
 | |
| 	ASSERT(fs_info);
 | |
| 	ret = btrfs_lookup_path(fs_info->fs_root, BTRFS_FIRST_FREE_OBJECTID,
 | |
| 				path, &root, &ino, &type, 40);
 | |
| 	if (ret < 0) {
 | |
| 		printf("Cannot lookup path %s\n", path);
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	if (type != BTRFS_FT_DIR) {
 | |
| 		error("Not a directory: %s", path);
 | |
| 		return -ENOENT;
 | |
| 	}
 | |
| 	ret = btrfs_iter_dir(root, ino, show_dir);
 | |
| 	if (ret < 0) {
 | |
| 		error("An error occurred while listing directory %s", path);
 | |
| 		return ret;
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int btrfs_exists(const char *file)
 | |
| {
 | |
| 	struct btrfs_fs_info *fs_info = current_fs_info;
 | |
| 	struct btrfs_root *root;
 | |
| 	u64 ino;
 | |
| 	u8 type;
 | |
| 	int ret;
 | |
| 
 | |
| 	ASSERT(fs_info);
 | |
| 
 | |
| 	ret = btrfs_lookup_path(fs_info->fs_root, BTRFS_FIRST_FREE_OBJECTID,
 | |
| 				file, &root, &ino, &type, 40);
 | |
| 	if (ret < 0)
 | |
| 		return 0;
 | |
| 
 | |
| 	if (type == BTRFS_FT_REG_FILE)
 | |
| 		return 1;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int btrfs_size(const char *file, loff_t *size)
 | |
| {
 | |
| 	struct btrfs_fs_info *fs_info = current_fs_info;
 | |
| 	struct btrfs_inode_item *ii;
 | |
| 	struct btrfs_root *root;
 | |
| 	struct btrfs_path path;
 | |
| 	struct btrfs_key key;
 | |
| 	u64 ino;
 | |
| 	u8 type;
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = btrfs_lookup_path(fs_info->fs_root, BTRFS_FIRST_FREE_OBJECTID,
 | |
| 				file, &root, &ino, &type, 40);
 | |
| 	if (ret < 0) {
 | |
| 		printf("Cannot lookup file %s\n", file);
 | |
| 		return ret;
 | |
| 	}
 | |
| 	if (type != BTRFS_FT_REG_FILE) {
 | |
| 		printf("Not a regular file: %s\n", file);
 | |
| 		return -ENOENT;
 | |
| 	}
 | |
| 	btrfs_init_path(&path);
 | |
| 	key.objectid = ino;
 | |
| 	key.type = BTRFS_INODE_ITEM_KEY;
 | |
| 	key.offset = 0;
 | |
| 
 | |
| 	ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
 | |
| 	if (ret < 0) {
 | |
| 		printf("Cannot lookup ino %llu\n", ino);
 | |
| 		return ret;
 | |
| 	}
 | |
| 	if (ret > 0) {
 | |
| 		printf("Ino %llu does not exist\n", ino);
 | |
| 		ret = -ENOENT;
 | |
| 		goto out;
 | |
| 	}
 | |
| 	ii = btrfs_item_ptr(path.nodes[0], path.slots[0],
 | |
| 			    struct btrfs_inode_item);
 | |
| 	*size = btrfs_inode_size(path.nodes[0], ii);
 | |
| out:
 | |
| 	btrfs_release_path(&path);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| int btrfs_read(const char *file, void *buf, loff_t offset, loff_t len,
 | |
| 	       loff_t *actread)
 | |
| {
 | |
| 	struct btrfs_fs_info *fs_info = current_fs_info;
 | |
| 	struct btrfs_root *root;
 | |
| 	loff_t real_size;
 | |
| 	u64 ino;
 | |
| 	u8 type;
 | |
| 	int ret;
 | |
| 
 | |
| 	ASSERT(fs_info);
 | |
| 	ret = btrfs_lookup_path(fs_info->fs_root, BTRFS_FIRST_FREE_OBJECTID,
 | |
| 				file, &root, &ino, &type, 40);
 | |
| 	if (ret < 0) {
 | |
| 		error("Cannot lookup file %s", file);
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	if (type != BTRFS_FT_REG_FILE) {
 | |
| 		error("Not a regular file: %s", file);
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	ret = btrfs_size(file, &real_size);
 | |
| 	if (ret < 0) {
 | |
| 		error("Failed to get inode size: %s", file);
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	if (!len || len > real_size - offset)
 | |
| 		len = real_size - offset;
 | |
| 
 | |
| 	ret = btrfs_file_read(root, ino, offset, len, buf);
 | |
| 	if (ret < 0) {
 | |
| 		error("An error occurred while reading file %s", file);
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	*actread = len;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| void btrfs_close(void)
 | |
| {
 | |
| 	if (current_fs_info) {
 | |
| 		close_ctree_fs_info(current_fs_info);
 | |
| 		current_fs_info = NULL;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| int btrfs_uuid(char *uuid_str)
 | |
| {
 | |
| #ifdef CONFIG_LIB_UUID
 | |
| 	if (current_fs_info)
 | |
| 		uuid_bin_to_str(current_fs_info->super_copy->fsid, uuid_str,
 | |
| 				UUID_STR_FORMAT_STD);
 | |
| 	return 0;
 | |
| #endif
 | |
| 	return -ENOSYS;
 | |
| }
 |