2018-05-06 21:58:06 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0+
|
2017-09-03 15:00:29 +00:00
|
|
|
/*
|
|
|
|
* BTRFS filesystem implementation for U-Boot
|
|
|
|
*
|
|
|
|
* 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
#include <malloc.h>
|
2020-05-10 17:39:52 +00:00
|
|
|
#include <uuid.h>
|
2017-09-03 15:00:29 +00:00
|
|
|
#include <linux/time.h>
|
2020-06-24 16:02:48 +00:00
|
|
|
#include "btrfs.h"
|
|
|
|
#include "crypto/hash.h"
|
2020-06-24 16:02:49 +00:00
|
|
|
#include "disk-io.h"
|
2017-09-03 15:00:29 +00:00
|
|
|
|
2020-06-24 16:03:01 +00:00
|
|
|
struct btrfs_fs_info *current_fs_info;
|
2017-09-03 15:00:29 +00:00
|
|
|
|
2020-06-24 16:03:06 +00:00
|
|
|
static int show_dir(struct btrfs_root *root, struct extent_buffer *eb,
|
|
|
|
struct btrfs_dir_item *di)
|
2017-09-03 15:00:29 +00:00
|
|
|
{
|
2020-06-24 16:03:06 +00:00
|
|
|
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] = "FILE",
|
|
|
|
[BTRFS_FT_DIR] = "DIR",
|
|
|
|
[BTRFS_FT_CHRDEV] = "CHRDEV",
|
|
|
|
[BTRFS_FT_BLKDEV] = "BLKDEV",
|
|
|
|
[BTRFS_FT_FIFO] = "FIFO",
|
|
|
|
[BTRFS_FT_SOCK] = "SOCK",
|
|
|
|
[BTRFS_FT_SYMLINK] = "SYMLINK",
|
|
|
|
[BTRFS_FT_XATTR] = "XATTR"
|
2017-09-03 15:00:29 +00:00
|
|
|
};
|
2020-06-24 16:03:06 +00:00
|
|
|
u8 type = btrfs_dir_type(eb, di);
|
|
|
|
char namebuf[BTRFS_NAME_LEN];
|
|
|
|
char *target = NULL;
|
|
|
|
char filetime[32];
|
2017-09-03 15:00:29 +00:00
|
|
|
time_t mtime;
|
2020-10-31 01:07:51 +00:00
|
|
|
int ret = 0;
|
2017-09-03 15:00:29 +00:00
|
|
|
|
2020-06-24 16:03:06 +00:00
|
|
|
btrfs_dir_item_key_to_cpu(eb, di, &key);
|
2017-09-03 15:00:29 +00:00
|
|
|
|
2020-06-24 16:03:06 +00:00
|
|
|
if (key.type == BTRFS_ROOT_ITEM_KEY) {
|
|
|
|
struct btrfs_root *subvol;
|
2017-09-03 15:00:29 +00:00
|
|
|
|
2020-06-24 16:03:06 +00:00
|
|
|
/* 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;
|
2017-09-03 15:00:29 +00:00
|
|
|
}
|
2020-06-24 16:03:06 +00:00
|
|
|
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);
|
2017-09-03 15:00:29 +00:00
|
|
|
|
2020-06-24 16:03:06 +00:00
|
|
|
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';
|
2017-09-03 15:00:29 +00:00
|
|
|
}
|
|
|
|
|
2020-06-24 16:03:06 +00:00
|
|
|
if (type < ARRAY_SIZE(dir_item_str) && dir_item_str[type])
|
|
|
|
printf("<%s> ", dir_item_str[type]);
|
2017-09-03 15:00:29 +00:00
|
|
|
else
|
2020-06-24 16:03:06 +00:00
|
|
|
printf("DIR_ITEM.%u", 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);
|
2017-09-03 15:00:29 +00:00
|
|
|
}
|
|
|
|
|
2020-06-24 16:03:06 +00:00
|
|
|
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 : "?");
|
2017-09-03 15:00:29 +00:00
|
|
|
printf("\n");
|
2020-06-24 16:03:06 +00:00
|
|
|
out:
|
|
|
|
free(target);
|
|
|
|
return ret;
|
2017-09-03 15:00:29 +00:00
|
|
|
}
|
|
|
|
|
2020-05-10 17:39:57 +00:00
|
|
|
int btrfs_probe(struct blk_desc *fs_dev_desc,
|
|
|
|
struct disk_partition *fs_partition)
|
2017-09-03 15:00:29 +00:00
|
|
|
{
|
2020-06-24 16:03:01 +00:00
|
|
|
struct btrfs_fs_info *fs_info;
|
|
|
|
int ret = -1;
|
|
|
|
|
2017-09-03 15:00:29 +00:00
|
|
|
btrfs_hash_init();
|
2020-06-24 16:03:01 +00:00
|
|
|
fs_info = open_ctree_fs_info(fs_dev_desc, fs_partition);
|
|
|
|
if (fs_info) {
|
|
|
|
current_fs_info = fs_info;
|
|
|
|
ret = 0;
|
|
|
|
}
|
|
|
|
return ret;
|
2017-09-03 15:00:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int btrfs_ls(const char *path)
|
|
|
|
{
|
2020-06-24 16:03:06 +00:00
|
|
|
struct btrfs_fs_info *fs_info = current_fs_info;
|
|
|
|
struct btrfs_root *root = fs_info->fs_root;
|
|
|
|
u64 ino = BTRFS_FIRST_FREE_OBJECTID;
|
2017-09-03 15:00:29 +00:00
|
|
|
u8 type;
|
2020-06-24 16:03:06 +00:00
|
|
|
int ret;
|
2017-09-03 15:00:29 +00:00
|
|
|
|
2020-06-24 16:03:06 +00:00
|
|
|
ASSERT(fs_info);
|
|
|
|
ret = btrfs_lookup_path(fs_info->fs_root, BTRFS_FIRST_FREE_OBJECTID,
|
|
|
|
path, &root, &ino, &type, 40);
|
|
|
|
if (ret < 0) {
|
2017-09-03 15:00:29 +00:00
|
|
|
printf("Cannot lookup path %s\n", path);
|
2020-06-24 16:03:06 +00:00
|
|
|
return ret;
|
2017-09-03 15:00:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (type != BTRFS_FT_DIR) {
|
2020-06-24 16:03:06 +00:00
|
|
|
error("Not a directory: %s", path);
|
|
|
|
return -ENOENT;
|
2017-09-03 15:00:29 +00:00
|
|
|
}
|
2020-06-24 16:03:06 +00:00
|
|
|
ret = btrfs_iter_dir(root, ino, show_dir);
|
|
|
|
if (ret < 0) {
|
2020-10-12 09:35:33 +00:00
|
|
|
error("An error occurred while listing directory %s", path);
|
2020-06-24 16:03:06 +00:00
|
|
|
return ret;
|
2017-09-03 15:00:29 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int btrfs_exists(const char *file)
|
|
|
|
{
|
2020-06-24 16:03:07 +00:00
|
|
|
struct btrfs_fs_info *fs_info = current_fs_info;
|
|
|
|
struct btrfs_root *root;
|
|
|
|
u64 ino;
|
2017-09-03 15:00:29 +00:00
|
|
|
u8 type;
|
2020-06-24 16:03:07 +00:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
ASSERT(fs_info);
|
2017-09-03 15:00:29 +00:00
|
|
|
|
2020-06-24 16:03:07 +00:00
|
|
|
ret = btrfs_lookup_path(fs_info->fs_root, BTRFS_FIRST_FREE_OBJECTID,
|
|
|
|
file, &root, &ino, &type, 40);
|
|
|
|
if (ret < 0)
|
|
|
|
return 0;
|
2017-09-03 15:00:29 +00:00
|
|
|
|
2020-06-24 16:03:07 +00:00
|
|
|
if (type == BTRFS_FT_REG_FILE)
|
|
|
|
return 1;
|
|
|
|
return 0;
|
2017-09-03 15:00:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int btrfs_size(const char *file, loff_t *size)
|
|
|
|
{
|
2020-06-24 16:03:07 +00:00
|
|
|
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;
|
2017-09-03 15:00:29 +00:00
|
|
|
u8 type;
|
2020-06-24 16:03:07 +00:00
|
|
|
int ret;
|
2017-09-03 15:00:29 +00:00
|
|
|
|
2020-06-24 16:03:07 +00:00
|
|
|
ret = btrfs_lookup_path(fs_info->fs_root, BTRFS_FIRST_FREE_OBJECTID,
|
|
|
|
file, &root, &ino, &type, 40);
|
|
|
|
if (ret < 0) {
|
2017-09-03 15:00:29 +00:00
|
|
|
printf("Cannot lookup file %s\n", file);
|
2020-06-24 16:03:07 +00:00
|
|
|
return ret;
|
2017-09-03 15:00:29 +00:00
|
|
|
}
|
|
|
|
if (type != BTRFS_FT_REG_FILE) {
|
|
|
|
printf("Not a regular file: %s\n", file);
|
2020-06-24 16:03:07 +00:00
|
|
|
return -ENOENT;
|
2017-09-03 15:00:29 +00:00
|
|
|
}
|
2020-06-24 16:03:07 +00:00
|
|
|
btrfs_init_path(&path);
|
|
|
|
key.objectid = ino;
|
|
|
|
key.type = BTRFS_INODE_ITEM_KEY;
|
|
|
|
key.offset = 0;
|
2017-09-03 15:00:29 +00:00
|
|
|
|
2020-06-24 16:03:07 +00:00
|
|
|
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;
|
2017-09-03 15:00:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int btrfs_read(const char *file, void *buf, loff_t offset, loff_t len,
|
|
|
|
loff_t *actread)
|
|
|
|
{
|
2020-06-24 16:03:11 +00:00
|
|
|
struct btrfs_fs_info *fs_info = current_fs_info;
|
|
|
|
struct btrfs_root *root;
|
|
|
|
loff_t real_size = 0;
|
|
|
|
u64 ino;
|
2017-09-03 15:00:29 +00:00
|
|
|
u8 type;
|
2020-06-24 16:03:11 +00:00
|
|
|
int ret;
|
2017-09-03 15:00:29 +00:00
|
|
|
|
2020-06-24 16:03:11 +00:00
|
|
|
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;
|
2017-09-03 15:00:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (type != BTRFS_FT_REG_FILE) {
|
2020-06-24 16:03:11 +00:00
|
|
|
error("Not a regular file: %s", file);
|
|
|
|
return -EINVAL;
|
2017-09-03 15:00:29 +00:00
|
|
|
}
|
|
|
|
|
2020-06-24 16:03:11 +00:00
|
|
|
if (!len) {
|
|
|
|
ret = btrfs_size(file, &real_size);
|
|
|
|
if (ret < 0) {
|
|
|
|
error("Failed to get inode size: %s", file);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
len = real_size;
|
|
|
|
}
|
2017-09-03 15:00:29 +00:00
|
|
|
|
2020-06-24 16:03:11 +00:00
|
|
|
if (len > real_size - offset)
|
|
|
|
len = real_size - offset;
|
2017-09-03 15:00:29 +00:00
|
|
|
|
2020-06-24 16:03:11 +00:00
|
|
|
ret = btrfs_file_read(root, ino, offset, len, buf);
|
|
|
|
if (ret < 0) {
|
2020-10-12 09:35:33 +00:00
|
|
|
error("An error occurred while reading file %s", file);
|
2020-06-24 16:03:11 +00:00
|
|
|
return ret;
|
2017-09-03 15:00:29 +00:00
|
|
|
}
|
|
|
|
|
2020-06-24 16:03:11 +00:00
|
|
|
*actread = len;
|
2017-09-03 15:00:29 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void btrfs_close(void)
|
|
|
|
{
|
2020-06-24 16:03:01 +00:00
|
|
|
if (current_fs_info) {
|
|
|
|
close_ctree_fs_info(current_fs_info);
|
|
|
|
current_fs_info = NULL;
|
|
|
|
}
|
2017-09-03 15:00:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int btrfs_uuid(char *uuid_str)
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_LIB_UUID
|
2020-06-24 16:03:07 +00:00
|
|
|
if (current_fs_info)
|
|
|
|
uuid_bin_to_str(current_fs_info->super_copy->fsid, uuid_str,
|
|
|
|
UUID_STR_FORMAT_STD);
|
2017-09-03 15:00:29 +00:00
|
|
|
return 0;
|
|
|
|
#endif
|
|
|
|
return -ENOSYS;
|
|
|
|
}
|