fs: btrfs: Add U-Boot fs handlers.
authorMarek BehĂșn <marek.behun@nic.cz>
Sun, 3 Sep 2017 15:00:29 +0000 (17:00 +0200)
committerTom Rini <trini@konsulko.com>
Tue, 3 Oct 2017 01:52:18 +0000 (21:52 -0400)
Signed-off-by: Marek Behun <marek.behun@nic.cz>
 create mode 100644 fs/btrfs/Kconfig
 create mode 100644 fs/btrfs/Makefile
 create mode 100644 fs/btrfs/btrfs.c
 create mode 100644 include/btrfs.h

fs/Kconfig
fs/Makefile
fs/btrfs/Kconfig [new file with mode: 0644]
fs/btrfs/Makefile [new file with mode: 0644]
fs/btrfs/btrfs.c [new file with mode: 0644]
fs/fs.c
include/btrfs.h [new file with mode: 0644]
include/fs.h

index e6803ac..1cb9831 100644 (file)
@@ -4,6 +4,8 @@
 
 menu "File systems"
 
+source "fs/btrfs/Kconfig"
+
 source "fs/cbfs/Kconfig"
 
 source "fs/ext4/Kconfig"
index b53c9d7..8a8175b 100644 (file)
@@ -12,6 +12,7 @@ obj-$(CONFIG_SPL_EXT_SUPPORT) += ext4/
 else
 obj-y                          += fs.o
 
+obj-$(CONFIG_FS_BTRFS) += btrfs/
 obj-$(CONFIG_FS_CBFS) += cbfs/
 obj-$(CONFIG_CMD_CRAMFS) += cramfs/
 obj-$(CONFIG_FS_EXT4) += ext4/
diff --git a/fs/btrfs/Kconfig b/fs/btrfs/Kconfig
new file mode 100644 (file)
index 0000000..22909d9
--- /dev/null
@@ -0,0 +1,9 @@
+config FS_BTRFS
+       bool "Enable BTRFS filesystem support"
+       select CRC32C
+       select LZO
+       select RBTREE
+       help
+         This provides a single-device read-only BTRFS support. BTRFS is a
+         next-generation Linux file system based on the copy-on-write
+         principle.
diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile
new file mode 100644 (file)
index 0000000..0173155
--- /dev/null
@@ -0,0 +1,8 @@
+#
+# 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz
+#
+# SPDX-License-Identifier:     GPL-2.0+
+#
+
+obj-y := btrfs.o chunk-map.o compression.o ctree.o dev.o dir-item.o \
+       extent-io.o hash.o inode.o root.o subvolume.o super.o
diff --git a/fs/btrfs/btrfs.c b/fs/btrfs/btrfs.c
new file mode 100644 (file)
index 0000000..4140e2b
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * BTRFS filesystem implementation for U-Boot
+ *
+ * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include "btrfs.h"
+#include <config.h>
+#include <malloc.h>
+#include <linux/time.h>
+
+struct btrfs_info btrfs_info;
+
+static int readdir_callback(const struct btrfs_root *root,
+                           struct btrfs_dir_item *item)
+{
+       static const char typestr[BTRFS_FT_MAX][4] = {
+               [BTRFS_FT_UNKNOWN]  = " ? ",
+               [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",
+               [BTRFS_FT_XATTR]    = " ? ",
+       };
+       struct btrfs_inode_item inode;
+       const char *name = (const char *) (item + 1);
+       char filetime[32], *target = NULL;
+       time_t mtime;
+
+       if (btrfs_lookup_inode(root, &item->location, &inode, NULL)) {
+               printf("%s: Cannot find inode item for directory entry %.*s!\n",
+                      __func__, item->name_len, name);
+               return 0;
+       }
+
+       mtime = inode.mtime.sec;
+       ctime_r(&mtime, filetime);
+
+       if (item->type == BTRFS_FT_SYMLINK) {
+               target = malloc(min(inode.size + 1,
+                                   (u64) btrfs_info.sb.sectorsize));
+
+               if (target && btrfs_readlink(root, item->location.objectid,
+                                            target)) {
+                       free(target);
+                       target = NULL;
+               }
+
+               if (!target)
+                       printf("%s: Cannot read symlink target!\n", __func__);
+       }
+
+       printf("<%s> ", typestr[item->type]);
+       if (item->type == BTRFS_FT_CHRDEV || item->type == BTRFS_FT_BLKDEV)
+               printf("%4u,%5u  ", (unsigned int) (inode.rdev >> 20),
+                       (unsigned int) (inode.rdev & 0xfffff));
+       else
+               printf("%10llu  ", inode.size);
+
+       printf("%24.24s  %.*s", filetime, item->name_len, name);
+
+       if (item->type == BTRFS_FT_SYMLINK) {
+               printf(" -> %s", target ? target : "?");
+               if (target)
+                       free(target);
+       }
+
+       printf("\n");
+
+       return 0;
+}
+
+int btrfs_probe(struct blk_desc *fs_dev_desc, disk_partition_t *fs_partition)
+{
+       btrfs_blk_desc = fs_dev_desc;
+       btrfs_part_info = fs_partition;
+
+       memset(&btrfs_info, 0, sizeof(btrfs_info));
+
+       btrfs_hash_init();
+       if (btrfs_read_superblock())
+               return -1;
+
+       if (btrfs_chunk_map_init()) {
+               printf("%s: failed to init chunk map\n", __func__);
+               return -1;
+       }
+
+       btrfs_info.tree_root.objectid = 0;
+       btrfs_info.tree_root.bytenr = btrfs_info.sb.root;
+       btrfs_info.chunk_root.objectid = 0;
+       btrfs_info.chunk_root.bytenr = btrfs_info.sb.chunk_root;
+
+       if (btrfs_read_chunk_tree()) {
+               printf("%s: failed to read chunk tree\n", __func__);
+               return -1;
+       }
+
+       if (btrfs_find_root(btrfs_get_default_subvol_objectid(),
+                           &btrfs_info.fs_root, NULL)) {
+               printf("%s: failed to find default subvolume\n", __func__);
+               return -1;
+       }
+
+       return 0;
+}
+
+int btrfs_ls(const char *path)
+{
+       struct btrfs_root root = btrfs_info.fs_root;
+       u64 inr;
+       u8 type;
+
+       inr = btrfs_lookup_path(&root, root.root_dirid, path, &type, NULL, 40);
+
+       if (inr == -1ULL) {
+               printf("Cannot lookup path %s\n", path);
+               return 1;
+       }
+
+       if (type != BTRFS_FT_DIR) {
+               printf("Not a directory: %s\n", path);
+               return 1;
+       }
+
+       if (btrfs_readdir(&root, inr, readdir_callback)) {
+               printf("An error occured while listing directory %s\n", path);
+               return 1;
+       }
+
+       return 0;
+}
+
+int btrfs_exists(const char *file)
+{
+       struct btrfs_root root = btrfs_info.fs_root;
+       u64 inr;
+       u8 type;
+
+       inr = btrfs_lookup_path(&root, root.root_dirid, file, &type, NULL, 40);
+
+       return (inr != -1ULL && type == BTRFS_FT_REG_FILE);
+}
+
+int btrfs_size(const char *file, loff_t *size)
+{
+       struct btrfs_root root = btrfs_info.fs_root;
+       struct btrfs_inode_item inode;
+       u64 inr;
+       u8 type;
+
+       inr = btrfs_lookup_path(&root, root.root_dirid, file, &type, &inode,
+                               40);
+
+       if (inr == -1ULL) {
+               printf("Cannot lookup file %s\n", file);
+               return 1;
+       }
+
+       if (type != BTRFS_FT_REG_FILE) {
+               printf("Not a regular file: %s\n", file);
+               return 1;
+       }
+
+       *size = inode.size;
+       return 0;
+}
+
+int btrfs_read(const char *file, void *buf, loff_t offset, loff_t len,
+              loff_t *actread)
+{
+       struct btrfs_root root = btrfs_info.fs_root;
+       struct btrfs_inode_item inode;
+       u64 inr, rd;
+       u8 type;
+
+       inr = btrfs_lookup_path(&root, root.root_dirid, file, &type, &inode,
+                               40);
+
+       if (inr == -1ULL) {
+               printf("Cannot lookup file %s\n", file);
+               return 1;
+       }
+
+       if (type != BTRFS_FT_REG_FILE) {
+               printf("Not a regular file: %s\n", file);
+               return 1;
+       }
+
+       if (!len)
+               len = inode.size;
+
+       if (len > inode.size - offset)
+               len = inode.size - offset;
+
+       rd = btrfs_file_read(&root, inr, offset, len, buf);
+       if (rd == -1ULL) {
+               printf("An error occured while reading file %s\n", file);
+               return 1;
+       }
+
+       *actread = rd;
+       return 0;
+}
+
+void btrfs_close(void)
+{
+       btrfs_chunk_map_exit();
+}
+
+int btrfs_uuid(char *uuid_str)
+{
+#ifdef CONFIG_LIB_UUID
+       uuid_bin_to_str(btrfs_info.sb.fsid, uuid_str, UUID_STR_FORMAT_STD);
+       return 0;
+#endif
+       return -ENOSYS;
+}
+
+/*
+               btrfs_list_subvols();
+*/
diff --git a/fs/fs.c b/fs/fs.c
index 3481229..84349f3 100644 (file)
--- a/fs/fs.c
+++ b/fs/fs.c
@@ -14,6 +14,7 @@
 #include <fs.h>
 #include <sandboxfs.h>
 #include <ubifs_uboot.h>
+#include <btrfs.h>
 #include <asm/io.h>
 #include <div64.h>
 #include <linux/math64.h>
@@ -219,6 +220,21 @@ static struct fstype_info fstypes[] = {
                .opendir = fs_opendir_unsupported,
        },
 #endif
+#ifdef CONFIG_FS_BTRFS
+       {
+               .fstype = FS_TYPE_BTRFS,
+               .name = "btrfs",
+               .null_dev_desc_ok = false,
+               .probe = btrfs_probe,
+               .close = btrfs_close,
+               .ls = btrfs_ls,
+               .exists = btrfs_exists,
+               .size = btrfs_size,
+               .read = btrfs_read,
+               .write = fs_write_unsupported,
+               .uuid = btrfs_uuid,
+       },
+#endif
        {
                .fstype = FS_TYPE_ANY,
                .name = "unsupported",
diff --git a/include/btrfs.h b/include/btrfs.h
new file mode 100644 (file)
index 0000000..7390975
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * BTRFS filesystem implementation for U-Boot
+ *
+ * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#ifndef __U_BOOT_BTRFS_H__
+#define __U_BOOT_BTRFS_H__
+
+int btrfs_probe(struct blk_desc *, disk_partition_t *);
+int btrfs_ls(const char *);
+int btrfs_exists(const char *);
+int btrfs_size(const char *, loff_t *);
+int btrfs_read(const char *, void *, loff_t, loff_t, loff_t *);
+void btrfs_close(void);
+int btrfs_uuid(char *);
+void btrfs_list_subvols(void);
+
+#endif /* __U_BOOT_BTRFS_H__ */
index 0869ad6..32fc480 100644 (file)
@@ -13,6 +13,7 @@
 #define FS_TYPE_EXT    2
 #define FS_TYPE_SANDBOX        3
 #define FS_TYPE_UBIFS  4
+#define FS_TYPE_BTRFS  5
 
 /*
  * Tell the fs layer which block device an partition to use for future