fs: btrfs: Add single-device read-only BTRFS implementation

This adds the proper implementation for the BTRFS filesystem.
The implementation currently supports only read-only mode and
the filesystem can be only on a single device.

Checksums of data chunks is unimplemented.

Compression is implemented (ZLIB + LZO).

Signed-off-by: Marek Behun <marek.behun@nic.cz>

 create mode 100644 fs/btrfs/btrfs.h
 create mode 100644 fs/btrfs/chunk-map.c
 create mode 100644 fs/btrfs/compression.c
 create mode 100644 fs/btrfs/ctree.c
 create mode 100644 fs/btrfs/dev.c
 create mode 100644 fs/btrfs/dir-item.c
 create mode 100644 fs/btrfs/extent-io.c
 create mode 100644 fs/btrfs/hash.c
 create mode 100644 fs/btrfs/inode.c
 create mode 100644 fs/btrfs/root.c
 create mode 100644 fs/btrfs/subvolume.c
 create mode 100644 fs/btrfs/super.c
diff --git a/fs/btrfs/root.c b/fs/btrfs/root.c
new file mode 100644
index 0000000..c405813
--- /dev/null
+++ b/fs/btrfs/root.c
@@ -0,0 +1,93 @@
+/*
+ * BTRFS filesystem implementation for U-Boot
+ *
+ * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include "btrfs.h"
+
+static void read_root_item(struct btrfs_path *p, struct btrfs_root_item *item)
+{
+	u32 len;
+	int reset = 0;
+
+	len = btrfs_path_item_size(p);
+	memcpy(item, btrfs_path_item_ptr(p, struct btrfs_root_item), len);
+	btrfs_root_item_to_cpu(item);
+
+	if (len < sizeof(*item))
+		reset = 1;
+	if (!reset && item->generation != item->generation_v2) {
+		if (item->generation_v2 != 0)
+			printf("%s: generation != generation_v2 in root item",
+			       __func__);
+		reset = 1;
+	}
+	if (reset) {
+		memset(&item->generation_v2, 0,
+		       sizeof(*item) - offsetof(struct btrfs_root_item,
+						generation_v2));
+	}
+}
+
+int btrfs_find_root(u64 objectid, struct btrfs_root *root,
+		    struct btrfs_root_item *root_item)
+{
+	struct btrfs_path path;
+	struct btrfs_root_item my_root_item;
+
+	if (!btrfs_search_tree_key_type(&btrfs_info.tree_root, objectid,
+					BTRFS_ROOT_ITEM_KEY, &path))
+		return -1;
+
+	if (!root_item)
+		root_item = &my_root_item;
+	read_root_item(&path, root_item);
+
+	if (root) {
+		root->objectid = objectid;
+		root->bytenr = root_item->bytenr;
+		root->root_dirid = root_item->root_dirid;
+	}
+
+	btrfs_free_path(&path);
+	return 0;
+}
+
+u64 btrfs_lookup_root_ref(u64 subvolid, struct btrfs_root_ref *refp, char *name)
+{
+	struct btrfs_path path;
+	struct btrfs_key *key;
+	struct btrfs_root_ref *ref;
+	u64 res = -1ULL;
+
+	key = btrfs_search_tree_key_type(&btrfs_info.tree_root, subvolid,
+					       BTRFS_ROOT_BACKREF_KEY, &path);
+
+	if (!key)
+		return -1ULL;
+
+	ref = btrfs_path_item_ptr(&path, struct btrfs_root_ref);
+	btrfs_root_ref_to_cpu(ref);
+
+	if (refp)
+		*refp = *ref;
+
+	if (name) {
+		if (ref->name_len > BTRFS_VOL_NAME_MAX) {
+			printf("%s: volume name too long: %u\n", __func__,
+			       ref->name_len);
+			goto out;
+		}
+
+		memcpy(name, ref + 1, ref->name_len);
+	}
+
+	res = key->offset;
+out:
+	btrfs_free_path(&path);
+	return res;
+}
+