aboutsummaryrefslogtreecommitdiffstats
path: root/roms/u-boot/fs
diff options
context:
space:
mode:
Diffstat (limited to 'roms/u-boot/fs')
-rw-r--r--roms/u-boot/fs/Kconfig27
-rw-r--r--roms/u-boot/fs/Makefile29
-rw-r--r--roms/u-boot/fs/btrfs/Kconfig11
-rw-r--r--roms/u-boot/fs/btrfs/Makefile7
-rw-r--r--roms/u-boot/fs/btrfs/btrfs.c288
-rw-r--r--roms/u-boot/fs/btrfs/btrfs.h28
-rw-r--r--roms/u-boot/fs/btrfs/common/rbtree-utils.c83
-rw-r--r--roms/u-boot/fs/btrfs/common/rbtree-utils.h53
-rw-r--r--roms/u-boot/fs/btrfs/compat.h82
-rw-r--r--roms/u-boot/fs/btrfs/compression.c214
-rw-r--r--roms/u-boot/fs/btrfs/conv-funcs.h175
-rw-r--r--roms/u-boot/fs/btrfs/crypto/hash.c55
-rw-r--r--roms/u-boot/fs/btrfs/crypto/hash.h17
-rw-r--r--roms/u-boot/fs/btrfs/ctree.c742
-rw-r--r--roms/u-boot/fs/btrfs/ctree.h1298
-rw-r--r--roms/u-boot/fs/btrfs/dev.c26
-rw-r--r--roms/u-boot/fs/btrfs/dir-item.c168
-rw-r--r--roms/u-boot/fs/btrfs/disk-io.c1058
-rw-r--r--roms/u-boot/fs/btrfs/disk-io.h50
-rw-r--r--roms/u-boot/fs/btrfs/extent-cache.c318
-rw-r--r--roms/u-boot/fs/btrfs/extent-cache.h104
-rw-r--r--roms/u-boot/fs/btrfs/extent-io.c809
-rw-r--r--roms/u-boot/fs/btrfs/extent-io.h164
-rw-r--r--roms/u-boot/fs/btrfs/inode.c767
-rw-r--r--roms/u-boot/fs/btrfs/kernel-shared/btrfs_tree.h1333
-rw-r--r--roms/u-boot/fs/btrfs/root-tree.c47
-rw-r--r--roms/u-boot/fs/btrfs/subvolume.c234
-rw-r--r--roms/u-boot/fs/btrfs/volumes.c1173
-rw-r--r--roms/u-boot/fs/btrfs/volumes.h204
-rw-r--r--roms/u-boot/fs/cbfs/Kconfig20
-rw-r--r--roms/u-boot/fs/cbfs/Makefile4
-rw-r--r--roms/u-boot/fs/cbfs/cbfs.c521
-rw-r--r--roms/u-boot/fs/cramfs/Kconfig7
-rw-r--r--roms/u-boot/fs/cramfs/Makefile7
-rw-r--r--roms/u-boot/fs/cramfs/cramfs.c387
-rw-r--r--roms/u-boot/fs/cramfs/uncompress.c83
-rw-r--r--roms/u-boot/fs/ext4/Kconfig13
-rw-r--r--roms/u-boot/fs/ext4/Makefile11
-rw-r--r--roms/u-boot/fs/ext4/crc16.c62
-rw-r--r--roms/u-boot/fs/ext4/crc16.h16
-rw-r--r--roms/u-boot/fs/ext4/dev.c65
-rw-r--r--roms/u-boot/fs/ext4/ext4_common.c2424
-rw-r--r--roms/u-boot/fs/ext4/ext4_common.h90
-rw-r--r--roms/u-boot/fs/ext4/ext4_journal.c666
-rw-r--r--roms/u-boot/fs/ext4/ext4_journal.h127
-rw-r--r--roms/u-boot/fs/ext4/ext4_write.c1069
-rw-r--r--roms/u-boot/fs/ext4/ext4fs.c304
-rw-r--r--roms/u-boot/fs/fat/Kconfig24
-rw-r--r--roms/u-boot/fs/fat/Makefile5
-rw-r--r--roms/u-boot/fs/fat/fat.c1368
-rw-r--r--roms/u-boot/fs/fat/fat_write.c1773
-rw-r--r--roms/u-boot/fs/fs.c958
-rw-r--r--roms/u-boot/fs/fs_internal.c96
-rw-r--r--roms/u-boot/fs/jffs2/Kconfig7
-rw-r--r--roms/u-boot/fs/jffs2/LICENCE30
-rw-r--r--roms/u-boot/fs/jffs2/Makefile12
-rw-r--r--roms/u-boot/fs/jffs2/compr_lzo.c401
-rw-r--r--roms/u-boot/fs/jffs2/compr_rtime.c87
-rw-r--r--roms/u-boot/fs/jffs2/compr_rubin.c122
-rw-r--r--roms/u-boot/fs/jffs2/compr_zlib.c48
-rw-r--r--roms/u-boot/fs/jffs2/jffs2_1pass.c1986
-rw-r--r--roms/u-boot/fs/jffs2/jffs2_private.h110
-rw-r--r--roms/u-boot/fs/jffs2/mergesort.c51
-rw-r--r--roms/u-boot/fs/jffs2/mini_inflate.c375
-rw-r--r--roms/u-boot/fs/jffs2/summary.h163
-rw-r--r--roms/u-boot/fs/reiserfs/Kconfig0
-rw-r--r--roms/u-boot/fs/reiserfs/Makefile10
-rw-r--r--roms/u-boot/fs/reiserfs/dev.c28
-rw-r--r--roms/u-boot/fs/reiserfs/mode_string.c124
-rw-r--r--roms/u-boot/fs/reiserfs/reiserfs.c971
-rw-r--r--roms/u-boot/fs/reiserfs/reiserfs_private.h509
-rw-r--r--roms/u-boot/fs/sandbox/Makefile11
-rw-r--r--roms/u-boot/fs/sandbox/sandboxfs.c144
-rw-r--r--roms/u-boot/fs/squashfs/Kconfig11
-rw-r--r--roms/u-boot/fs/squashfs/Makefile7
-rw-r--r--roms/u-boot/fs/squashfs/sqfs.c1723
-rw-r--r--roms/u-boot/fs/squashfs/sqfs_decompressor.c155
-rw-r--r--roms/u-boot/fs/squashfs/sqfs_decompressor.h61
-rw-r--r--roms/u-boot/fs/squashfs/sqfs_dir.c93
-rw-r--r--roms/u-boot/fs/squashfs/sqfs_filesystem.h310
-rw-r--r--roms/u-boot/fs/squashfs/sqfs_inode.c162
-rw-r--r--roms/u-boot/fs/squashfs/sqfs_utils.h49
-rw-r--r--roms/u-boot/fs/ubifs/Kconfig6
-rw-r--r--roms/u-boot/fs/ubifs/Makefile13
-rw-r--r--roms/u-boot/fs/ubifs/budget.c729
-rw-r--r--roms/u-boot/fs/ubifs/crc16.c60
-rw-r--r--roms/u-boot/fs/ubifs/crc16.h29
-rw-r--r--roms/u-boot/fs/ubifs/debug.c3151
-rw-r--r--roms/u-boot/fs/ubifs/debug.h369
-rw-r--r--roms/u-boot/fs/ubifs/gc.c977
-rw-r--r--roms/u-boot/fs/ubifs/io.c1163
-rw-r--r--roms/u-boot/fs/ubifs/key.h536
-rw-r--r--roms/u-boot/fs/ubifs/log.c746
-rw-r--r--roms/u-boot/fs/ubifs/lprops.c1314
-rw-r--r--roms/u-boot/fs/ubifs/lpt.c2285
-rw-r--r--roms/u-boot/fs/ubifs/lpt_commit.c2044
-rw-r--r--roms/u-boot/fs/ubifs/master.c394
-rw-r--r--roms/u-boot/fs/ubifs/misc.h300
-rw-r--r--roms/u-boot/fs/ubifs/orphan.c947
-rw-r--r--roms/u-boot/fs/ubifs/recovery.c1557
-rw-r--r--roms/u-boot/fs/ubifs/replay.c1077
-rw-r--r--roms/u-boot/fs/ubifs/sb.c821
-rw-r--r--roms/u-boot/fs/ubifs/scan.c373
-rw-r--r--roms/u-boot/fs/ubifs/super.c2712
-rw-r--r--roms/u-boot/fs/ubifs/tnc.c3330
-rw-r--r--roms/u-boot/fs/ubifs/tnc_misc.c487
-rw-r--r--roms/u-boot/fs/ubifs/ubifs-media.h784
-rw-r--r--roms/u-boot/fs/ubifs/ubifs.c950
-rw-r--r--roms/u-boot/fs/ubifs/ubifs.h2409
-rw-r--r--roms/u-boot/fs/yaffs2/Kconfig7
-rw-r--r--roms/u-boot/fs/yaffs2/Makefile22
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_allocator.c357
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_allocator.h30
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_attribs.c152
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_attribs.h28
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_bitmap.c97
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_bitmap.h33
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_checkptrw.c409
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_checkptrw.h33
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_ecc.c281
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_ecc.h44
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_error.c58
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_flashif.h35
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_flashif2.h35
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_getblockinfo.h35
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_guts.c5009
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_guts.h973
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_mtdif.c165
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_mtdif.h27
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_mtdif2.c233
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_mtdif2.h30
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_nameval.c208
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_nameval.h28
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_nand.c120
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_nand.h38
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_nandemul2k.h39
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_nandif.c252
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_nandif.h65
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_osglue.h41
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_packedtags1.c56
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_packedtags1.h39
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_packedtags2.c197
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_packedtags2.h47
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_qsort.c141
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_summary.c310
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_summary.h37
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_tagscompat.c407
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_tagscompat.h36
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_trace.h57
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_uboot_glue.c466
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_verify.c522
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_verify.h43
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_yaffs1.c420
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_yaffs1.h22
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_yaffs2.c1527
-rw-r--r--roms/u-boot/fs/yaffs2/yaffs_yaffs2.h39
-rw-r--r--roms/u-boot/fs/yaffs2/yaffscfg.h38
-rw-r--r--roms/u-boot/fs/yaffs2/yaffsfs.c3209
-rw-r--r--roms/u-boot/fs/yaffs2/yaffsfs.h209
-rw-r--r--roms/u-boot/fs/yaffs2/ydirectenv.h82
-rw-r--r--roms/u-boot/fs/yaffs2/yportenv.h310
-rw-r--r--roms/u-boot/fs/zfs/Makefile6
-rw-r--r--roms/u-boot/fs/zfs/dev.c30
-rw-r--r--roms/u-boot/fs/zfs/zfs.c2341
-rw-r--r--roms/u-boot/fs/zfs/zfs_fletcher.c74
-rw-r--r--roms/u-boot/fs/zfs/zfs_lzjb.c84
-rw-r--r--roms/u-boot/fs/zfs/zfs_sha256.c135
167 files changed, 76755 insertions, 0 deletions
diff --git a/roms/u-boot/fs/Kconfig b/roms/u-boot/fs/Kconfig
new file mode 100644
index 000000000..620af7f04
--- /dev/null
+++ b/roms/u-boot/fs/Kconfig
@@ -0,0 +1,27 @@
+#
+# File system configuration
+#
+
+menu "File systems"
+
+source "fs/btrfs/Kconfig"
+
+source "fs/cbfs/Kconfig"
+
+source "fs/ext4/Kconfig"
+
+source "fs/reiserfs/Kconfig"
+
+source "fs/fat/Kconfig"
+
+source "fs/jffs2/Kconfig"
+
+source "fs/ubifs/Kconfig"
+
+source "fs/cramfs/Kconfig"
+
+source "fs/yaffs2/Kconfig"
+
+source "fs/squashfs/Kconfig"
+
+endmenu
diff --git a/roms/u-boot/fs/Makefile b/roms/u-boot/fs/Makefile
new file mode 100644
index 000000000..937cbcf6e
--- /dev/null
+++ b/roms/u-boot/fs/Makefile
@@ -0,0 +1,29 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright 2000-2006
+# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+# Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+
+ifdef CONFIG_SPL_BUILD
+obj-$(CONFIG_FS_LOADER) += fs.o
+obj-$(CONFIG_SPL_FS_FAT) += fat/
+obj-$(CONFIG_SPL_FS_EXT4) += ext4/
+obj-$(CONFIG_SPL_FS_CBFS) += cbfs/
+obj-$(CONFIG_SPL_FS_SQUASHFS) += squashfs/
+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/
+obj-$(CONFIG_FS_FAT) += fat/
+obj-$(CONFIG_FS_JFFS2) += jffs2/
+obj-$(CONFIG_CMD_REISER) += reiserfs/
+obj-$(CONFIG_SANDBOX) += sandbox/
+obj-$(CONFIG_CMD_UBIFS) += ubifs/
+obj-$(CONFIG_YAFFS2) += yaffs2/
+obj-$(CONFIG_CMD_ZFS) += zfs/
+obj-$(CONFIG_FS_SQUASHFS) += squashfs/
+endif
+obj-y += fs_internal.o
diff --git a/roms/u-boot/fs/btrfs/Kconfig b/roms/u-boot/fs/btrfs/Kconfig
new file mode 100644
index 000000000..2a32f42ad
--- /dev/null
+++ b/roms/u-boot/fs/btrfs/Kconfig
@@ -0,0 +1,11 @@
+config FS_BTRFS
+ bool "Enable BTRFS filesystem support"
+ select CRC32C
+ select LZO
+ select ZSTD
+ select RBTREE
+ select SHA256
+ 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/roms/u-boot/fs/btrfs/Makefile b/roms/u-boot/fs/btrfs/Makefile
new file mode 100644
index 000000000..fc074c84d
--- /dev/null
+++ b/roms/u-boot/fs/btrfs/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz
+
+obj-y := btrfs.o compression.o ctree.o dev.o dir-item.o \
+ extent-io.o inode.o subvolume.o crypto/hash.o disk-io.o \
+ common/rbtree-utils.o extent-cache.o volumes.o root-tree.o
diff --git a/roms/u-boot/fs/btrfs/btrfs.c b/roms/u-boot/fs/btrfs/btrfs.c
new file mode 100644
index 000000000..52a243a65
--- /dev/null
+++ b/roms/u-boot/fs/btrfs/btrfs.c
@@ -0,0 +1,288 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * BTRFS filesystem implementation for U-Boot
+ *
+ * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz
+ */
+
+#include <config.h>
+#include <malloc.h>
+#include <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 = 0;
+ 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;
+ }
+
+ if (!len) {
+ ret = btrfs_size(file, &real_size);
+ if (ret < 0) {
+ error("Failed to get inode size: %s", file);
+ return ret;
+ }
+ len = real_size;
+ }
+
+ if (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;
+}
diff --git a/roms/u-boot/fs/btrfs/btrfs.h b/roms/u-boot/fs/btrfs/btrfs.h
new file mode 100644
index 000000000..7d8b395b2
--- /dev/null
+++ b/roms/u-boot/fs/btrfs/btrfs.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * BTRFS filesystem implementation for U-Boot
+ *
+ * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz
+ */
+
+#ifndef __BTRFS_BTRFS_H__
+#define __BTRFS_BTRFS_H__
+
+#include <linux/rbtree.h>
+#include "conv-funcs.h"
+
+extern struct btrfs_info btrfs_info;
+extern struct btrfs_fs_info *current_fs_info;
+
+/* compression.c */
+u32 btrfs_decompress(u8 type, const char *, u32, char *, u32);
+
+/* inode.c */
+int btrfs_readlink(struct btrfs_root *root, u64 ino, char *target);
+int btrfs_file_read(struct btrfs_root *root, u64 ino, u64 file_offset, u64 len,
+ char *dest);
+
+/* subvolume.c */
+u64 btrfs_get_default_subvol_objectid(void);
+
+#endif /* !__BTRFS_BTRFS_H__ */
diff --git a/roms/u-boot/fs/btrfs/common/rbtree-utils.c b/roms/u-boot/fs/btrfs/common/rbtree-utils.c
new file mode 100644
index 000000000..7a7d7e84e
--- /dev/null
+++ b/roms/u-boot/fs/btrfs/common/rbtree-utils.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2014 Facebook. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#include <linux/errno.h>
+#include "rbtree-utils.h"
+
+int rb_insert(struct rb_root *root, struct rb_node *node,
+ rb_compare_nodes comp)
+{
+ struct rb_node **p = &root->rb_node;
+ struct rb_node *parent = NULL;
+ int ret;
+
+ while(*p) {
+ parent = *p;
+
+ ret = comp(parent, node);
+ if (ret < 0)
+ p = &(*p)->rb_left;
+ else if (ret > 0)
+ p = &(*p)->rb_right;
+ else
+ return -EEXIST;
+ }
+
+ rb_link_node(node, parent, p);
+ rb_insert_color(node, root);
+ return 0;
+}
+
+struct rb_node *rb_search(struct rb_root *root, void *key, rb_compare_keys comp,
+ struct rb_node **next_ret)
+{
+ struct rb_node *n = root->rb_node;
+ struct rb_node *parent = NULL;
+ int ret = 0;
+
+ while(n) {
+ parent = n;
+
+ ret = comp(n, key);
+ if (ret < 0)
+ n = n->rb_left;
+ else if (ret > 0)
+ n = n->rb_right;
+ else
+ return n;
+ }
+
+ if (!next_ret)
+ return NULL;
+
+ if (parent && ret > 0)
+ parent = rb_next(parent);
+
+ *next_ret = parent;
+ return NULL;
+}
+
+void rb_free_nodes(struct rb_root *root, rb_free_node free_node)
+{
+ struct rb_node *node;
+
+ while ((node = rb_first(root))) {
+ rb_erase(node, root);
+ free_node(node);
+ }
+}
diff --git a/roms/u-boot/fs/btrfs/common/rbtree-utils.h b/roms/u-boot/fs/btrfs/common/rbtree-utils.h
new file mode 100644
index 000000000..d977cfd95
--- /dev/null
+++ b/roms/u-boot/fs/btrfs/common/rbtree-utils.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2014 Facebook. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#ifndef __RBTREE_UTILS__
+#define __RBTREE_UTILS__
+
+#include <linux/rbtree.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The common insert/search/free functions */
+typedef int (*rb_compare_nodes)(struct rb_node *node1, struct rb_node *node2);
+typedef int (*rb_compare_keys)(struct rb_node *node, void *key);
+typedef void (*rb_free_node)(struct rb_node *node);
+
+int rb_insert(struct rb_root *root, struct rb_node *node,
+ rb_compare_nodes comp);
+/*
+ * In some cases, we need return the next node if we don't find the node we
+ * specify. At this time, we can use next_ret.
+ */
+struct rb_node *rb_search(struct rb_root *root, void *key, rb_compare_keys comp,
+ struct rb_node **next_ret);
+void rb_free_nodes(struct rb_root *root, rb_free_node free_node);
+
+#define FREE_RB_BASED_TREE(name, free_func) \
+static void free_##name##_tree(struct rb_root *root) \
+{ \
+ rb_free_nodes(root, free_func); \
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/roms/u-boot/fs/btrfs/compat.h b/roms/u-boot/fs/btrfs/compat.h
new file mode 100644
index 000000000..9cf8a10c7
--- /dev/null
+++ b/roms/u-boot/fs/btrfs/compat.h
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#ifndef __BTRFS_COMPAT_H__
+#define __BTRFS_COMPAT_H__
+
+#include <linux/errno.h>
+#include <fs_internal.h>
+#include <uuid.h>
+
+/* Provide a compatibility layer to make code syncing easier */
+
+/* A simple wraper to for error() used in btrfs-progs */
+#define error(fmt, ...) pr_err("BTRFS: " fmt "\n", ##__VA_ARGS__)
+
+#define ASSERT(c) assert(c)
+
+#define BTRFS_UUID_UNPARSED_SIZE 37
+
+/* No <linux/limits.h> so have to define it here */
+#define XATTR_NAME_MAX 255
+#define PATH_MAX 4096
+
+/*
+ * Macros to generate set/get funcs for the struct fields
+ * assume there is a lefoo_to_cpu for every type, so lets make a simple
+ * one for u8:
+ */
+#define le8_to_cpu(v) (v)
+#define cpu_to_le8(v) (v)
+#define __le8 u8
+
+/*
+ * Macros to generate set/get funcs for the struct fields
+ * assume there is a lefoo_to_cpu for every type, so lets make a simple
+ * one for u8:
+ */
+#define le8_to_cpu(v) (v)
+#define cpu_to_le8(v) (v)
+#define __le8 u8
+
+#define get_unaligned_le8(p) (*((u8 *)(p)))
+#define get_unaligned_8(p) (*((u8 *)(p)))
+#define put_unaligned_le8(val,p) ((*((u8 *)(p))) = (val))
+#define put_unaligned_8(val,p) ((*((u8 *)(p))) = (val))
+
+/*
+ * Read data from device specified by @desc and @part
+ *
+ * U-boot equivalent of pread().
+ *
+ * Return the bytes of data read.
+ * Return <0 for error.
+ */
+static inline int __btrfs_devread(struct blk_desc *desc,
+ struct disk_partition *part,
+ void *buf, size_t size, u64 offset)
+{
+ lbaint_t sector;
+ int byte_offset;
+ int ret;
+
+ sector = offset >> desc->log2blksz;
+ byte_offset = offset % desc->blksz;
+
+ /* fs_devread() return 0 for error, >0 for success */
+ ret = fs_devread(desc, part, sector, byte_offset, size, buf);
+ if (!ret)
+ return -EIO;
+ return size;
+}
+
+static inline void uuid_unparse(const u8 *uuid, char *out)
+{
+ return uuid_bin_to_str((unsigned char *)uuid, out, 0);
+}
+
+static inline int is_power_of_2(unsigned long n)
+{
+ return (n != 0 && ((n & (n - 1)) == 0));
+}
+
+#endif
diff --git a/roms/u-boot/fs/btrfs/compression.c b/roms/u-boot/fs/btrfs/compression.c
new file mode 100644
index 000000000..23efefa19
--- /dev/null
+++ b/roms/u-boot/fs/btrfs/compression.c
@@ -0,0 +1,214 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * BTRFS filesystem implementation for U-Boot
+ *
+ * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz
+ */
+
+#include "btrfs.h"
+#include <log.h>
+#include <malloc.h>
+#include <linux/lzo.h>
+#include <linux/zstd.h>
+#include <linux/compat.h>
+#include <u-boot/zlib.h>
+#include <asm/unaligned.h>
+
+/* Header for each segment, LE32, recording the compressed size */
+#define LZO_LEN 4
+static u32 decompress_lzo(const u8 *cbuf, u32 clen, u8 *dbuf, u32 dlen)
+{
+ u32 tot_len, tot_in, in_len, res;
+ size_t out_len;
+ int ret;
+
+ if (clen < LZO_LEN)
+ return -1;
+
+ tot_len = le32_to_cpu(get_unaligned((u32 *)cbuf));
+ tot_in = 0;
+ cbuf += LZO_LEN;
+ clen -= LZO_LEN;
+ tot_len -= LZO_LEN;
+ tot_in += LZO_LEN;
+
+ if (tot_len == 0 && dlen)
+ return -1;
+ if (tot_len < LZO_LEN)
+ return -1;
+
+ res = 0;
+
+ while (tot_len > LZO_LEN) {
+ u32 rem_page;
+
+ in_len = le32_to_cpu(get_unaligned((u32 *)cbuf));
+ cbuf += LZO_LEN;
+ clen -= LZO_LEN;
+
+ if (in_len > clen || tot_len < LZO_LEN + in_len)
+ return -1;
+
+ tot_len -= (LZO_LEN + in_len);
+ tot_in += (LZO_LEN + in_len);
+
+ out_len = dlen;
+ ret = lzo1x_decompress_safe(cbuf, in_len, dbuf, &out_len);
+ if (ret != LZO_E_OK)
+ return -1;
+
+ cbuf += in_len;
+ clen -= in_len;
+ dbuf += out_len;
+ dlen -= out_len;
+
+ res += out_len;
+
+ /*
+ * If the 4 bytes header does not fit to the rest of the page we
+ * have to move to next one, or we read some garbage.
+ */
+ rem_page = PAGE_SIZE - (tot_in % PAGE_SIZE);
+ if (rem_page < LZO_LEN) {
+ cbuf += rem_page;
+ tot_in += rem_page;
+ clen -= rem_page;
+ tot_len -= rem_page;
+ }
+ }
+
+ return res;
+}
+
+/* from zutil.h */
+#define PRESET_DICT 0x20
+
+static u32 decompress_zlib(const u8 *_cbuf, u32 clen, u8 *dbuf, u32 dlen)
+{
+ int wbits = MAX_WBITS, ret = -1;
+ z_stream stream;
+ u8 *cbuf;
+ u32 res;
+
+ memset(&stream, 0, sizeof(stream));
+
+ cbuf = (u8 *) _cbuf;
+
+ stream.total_in = 0;
+
+ stream.next_out = dbuf;
+ stream.avail_out = dlen;
+ stream.total_out = 0;
+
+ /* skip adler32 check if deflate and no dictionary */
+ if (clen > 2 && !(cbuf[1] & PRESET_DICT) &&
+ ((cbuf[0] & 0x0f) == Z_DEFLATED) &&
+ !(((cbuf[0] << 8) + cbuf[1]) % 31)) {
+ wbits = -((cbuf[0] >> 4) + 8);
+ cbuf += 2;
+ clen -= 2;
+ }
+
+ if (Z_OK != inflateInit2(&stream, wbits))
+ return -1;
+
+ while (stream.total_in < clen) {
+ stream.next_in = cbuf + stream.total_in;
+ stream.avail_in = min((u32) (clen - stream.total_in),
+ current_fs_info->sectorsize);
+
+ ret = inflate(&stream, Z_NO_FLUSH);
+ if (ret != Z_OK)
+ break;
+ }
+
+ res = stream.total_out;
+ inflateEnd(&stream);
+
+ if (ret != Z_STREAM_END)
+ return -1;
+
+ return res;
+}
+
+#define ZSTD_BTRFS_MAX_WINDOWLOG 17
+#define ZSTD_BTRFS_MAX_INPUT (1 << ZSTD_BTRFS_MAX_WINDOWLOG)
+
+static u32 decompress_zstd(const u8 *cbuf, u32 clen, u8 *dbuf, u32 dlen)
+{
+ ZSTD_DStream *dstream;
+ ZSTD_inBuffer in_buf;
+ ZSTD_outBuffer out_buf;
+ void *workspace;
+ size_t wsize;
+ u32 res = -1;
+
+ wsize = ZSTD_DStreamWorkspaceBound(ZSTD_BTRFS_MAX_INPUT);
+ workspace = malloc(wsize);
+ if (!workspace) {
+ debug("%s: cannot allocate workspace of size %zu\n", __func__,
+ wsize);
+ return -1;
+ }
+
+ dstream = ZSTD_initDStream(ZSTD_BTRFS_MAX_INPUT, workspace, wsize);
+ if (!dstream) {
+ printf("%s: ZSTD_initDStream failed\n", __func__);
+ goto err_free;
+ }
+
+ in_buf.src = cbuf;
+ in_buf.pos = 0;
+ in_buf.size = clen;
+
+ out_buf.dst = dbuf;
+ out_buf.pos = 0;
+ out_buf.size = dlen;
+
+ while (1) {
+ size_t ret;
+
+ ret = ZSTD_decompressStream(dstream, &out_buf, &in_buf);
+ if (ZSTD_isError(ret)) {
+ printf("%s: ZSTD_decompressStream error %d\n", __func__,
+ ZSTD_getErrorCode(ret));
+ goto err_free;
+ }
+
+ if (in_buf.pos >= clen || !ret)
+ break;
+ }
+
+ res = out_buf.pos;
+
+err_free:
+ free(workspace);
+ return res;
+}
+
+u32 btrfs_decompress(u8 type, const char *c, u32 clen, char *d, u32 dlen)
+{
+ u32 res;
+ const u8 *cbuf;
+ u8 *dbuf;
+
+ cbuf = (const u8 *) c;
+ dbuf = (u8 *) d;
+
+ switch (type) {
+ case BTRFS_COMPRESS_NONE:
+ res = dlen < clen ? dlen : clen;
+ memcpy(dbuf, cbuf, res);
+ return res;
+ case BTRFS_COMPRESS_ZLIB:
+ return decompress_zlib(cbuf, clen, dbuf, dlen);
+ case BTRFS_COMPRESS_LZO:
+ return decompress_lzo(cbuf, clen, dbuf, dlen);
+ case BTRFS_COMPRESS_ZSTD:
+ return decompress_zstd(cbuf, clen, dbuf, dlen);
+ default:
+ printf("%s: Unsupported compression in extent: %i\n", __func__,
+ type);
+ return -1;
+ }
+}
diff --git a/roms/u-boot/fs/btrfs/conv-funcs.h b/roms/u-boot/fs/btrfs/conv-funcs.h
new file mode 100644
index 000000000..30791cd62
--- /dev/null
+++ b/roms/u-boot/fs/btrfs/conv-funcs.h
@@ -0,0 +1,175 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Functions to convert BTRFS structures from disk to CPU endianness and back.
+ *
+ * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz
+ */
+
+#ifndef __BTRFS_CONV_FUNCS_H__
+#define __BTRFS_CONV_FUNCS_H__
+
+#include "ctree.h"
+#include <u-boot/variadic-macro.h>
+#include <asm/byteorder.h>
+
+/* We are using variadic macros and C11 _Generic to achieve compact code.
+
+ We want to define macro DEFINE_CONV(x, ...), where the first argument is the
+ name of the structure for which it shall define conversion functions (the
+ names of the functions shall be x_to_cpu and x_to_disk), and the other
+ arguments are names of the members on which the functions shall do
+ endianness conversion. */
+
+#if defined(__LITTLE_ENDIAN)
+
+/* If the target machine is little endian, the conversion functions do
+ nothing, since the on disk format is little endian. */
+
+# define DEFINE_CONV(n,...) \
+ static inline struct n *n##_to_disk(struct n * r) \
+ { \
+ return r; \
+ } \
+ static inline struct n *n##_to_cpu(struct n * r) \
+ { \
+ return r; \
+ }
+
+# define DEFINE_CONV_ALT(n,a,...) \
+ static inline struct n *n##_to_disk_##a(struct n * r) \
+ { \
+ return r; \
+ } \
+ static inline struct n *n##_to_cpu_##a(struct n * r) \
+ { \
+ return r; \
+ }
+
+#else /* !defined(__LITTLE_ENDIAN) */
+
+/* Some structures contain not only scalar members, but compound types as well
+ (for example, struct btrfs_inode_item contains members of type struct
+ btrfs_timespec.
+
+ For these members we want to call the conversion function recursively, so
+ first we declare the functions taking pointers to this types (these function
+ will be defined later by the DEFINE_CONV macro) and then we define
+ correspond functions taking non-pointers, so that they can be used in the
+ expansion of the _Generic. */
+# define DEFINE_CONV_FOR_STRUCT(n) \
+ static inline struct n * n##_to_disk(struct n *); \
+ static inline struct n * n##_to_cpu(struct n *); \
+ static inline struct n n##_to_disk_v(struct n x) { \
+ return *n##_to_disk(&x); \
+ } \
+ static inline struct n n##_to_cpu_v(struct n x) { \
+ return *n##_to_cpu(&x); \
+ }
+
+DEFINE_CONV_FOR_STRUCT(btrfs_key)
+DEFINE_CONV_FOR_STRUCT(btrfs_stripe)
+DEFINE_CONV_FOR_STRUCT(btrfs_timespec)
+DEFINE_CONV_FOR_STRUCT(btrfs_inode_item)
+DEFINE_CONV_FOR_STRUCT(btrfs_root_backup)
+DEFINE_CONV_FOR_STRUCT(btrfs_dev_item)
+
+/* Now define the _Generic for both CPU to LE and LE to CPU */
+# define DEFINE_CONV_CPU_TO_LE(x) \
+ (d->x) = _Generic((d->x), \
+ __u16: cpu_to_le16, \
+ __u32: cpu_to_le32, \
+ __u64: cpu_to_le64, \
+ struct btrfs_key: btrfs_key_to_disk_v, \
+ struct btrfs_stripe: btrfs_stripe_to_disk_v, \
+ struct btrfs_timespec: btrfs_timespec_to_disk_v, \
+ struct btrfs_inode_item: btrfs_inode_item_to_disk_v, \
+ struct btrfs_root_backup: btrfs_root_backup_to_disk_v, \
+ struct btrfs_dev_item: btrfs_dev_item_to_disk_v \
+ )((d->x));
+
+# define DEFINE_CONV_LE_TO_CPU(x) \
+ (d->x) = _Generic((d->x), \
+ __u16: le16_to_cpu, \
+ __u32: le32_to_cpu, \
+ __u64: le64_to_cpu, \
+ struct btrfs_key: btrfs_key_to_cpu_v, \
+ struct btrfs_stripe: btrfs_stripe_to_cpu_v, \
+ struct btrfs_timespec: btrfs_timespec_to_cpu_v, \
+ struct btrfs_inode_item: btrfs_inode_item_to_cpu_v, \
+ struct btrfs_root_backup: btrfs_root_backup_to_cpu_v, \
+ struct btrfs_dev_item: btrfs_dev_item_to_cpu_v \
+ )((d->x));
+
+# define DEFINE_CONV_ONE(t,n,m,...) \
+ static inline struct t * n(struct t * d) { \
+ CALL_MACRO_FOR_EACH(m, ##__VA_ARGS__) \
+ return d; \
+ }
+
+/* Finally define the DEFINE_CONV macro */
+# define DEFINE_CONV(n,...) \
+ DEFINE_CONV_ONE(n,n##_to_disk,DEFINE_CONV_CPU_TO_LE,##__VA_ARGS__) \
+ DEFINE_CONV_ONE(n,n##_to_cpu,DEFINE_CONV_LE_TO_CPU,##__VA_ARGS__)
+
+# define DEFINE_CONV_ALT(n,a,...) \
+ DEFINE_CONV_ONE(n,n##_to_disk_##a,DEFINE_CONV_CPU_TO_LE, \
+ ##__VA_ARGS__) \
+ DEFINE_CONV_ONE(n,n##_to_cpu_##a,DEFINE_CONV_LE_TO_CPU,##__VA_ARGS__)
+
+#endif /* !defined(__LITTLE_ENDIAN) */
+
+DEFINE_CONV(btrfs_key, objectid, offset)
+DEFINE_CONV(btrfs_dev_item, devid, total_bytes, bytes_used, io_align, io_width,
+ sector_size, type, generation, start_offset, dev_group)
+DEFINE_CONV(btrfs_stripe, devid, offset)
+DEFINE_CONV(btrfs_chunk, length, owner, stripe_len, type, io_align, io_width,
+ sector_size, num_stripes, sub_stripes)
+DEFINE_CONV(btrfs_free_space_entry, offset, bytes)
+DEFINE_CONV(btrfs_free_space_header, location, generation, num_entries,
+ num_bitmaps)
+DEFINE_CONV(btrfs_extent_item, refs, generation, flags)
+DEFINE_CONV(btrfs_tree_block_info, key)
+DEFINE_CONV(btrfs_extent_data_ref, root, objectid, offset, count)
+DEFINE_CONV(btrfs_shared_data_ref, count)
+DEFINE_CONV(btrfs_extent_inline_ref, offset)
+DEFINE_CONV(btrfs_dev_extent, chunk_tree, chunk_objectid, chunk_offset, length)
+DEFINE_CONV(btrfs_inode_ref, index, name_len)
+DEFINE_CONV(btrfs_inode_extref, parent_objectid, index, name_len)
+DEFINE_CONV(btrfs_timespec, sec, nsec)
+DEFINE_CONV(btrfs_inode_item, generation, transid, size, nbytes, block_group,
+ nlink, uid, gid, mode, rdev, flags, sequence, atime, ctime, mtime,
+ otime)
+DEFINE_CONV(btrfs_dir_log_item, end)
+DEFINE_CONV(btrfs_dir_item, location, transid, data_len, name_len)
+DEFINE_CONV(btrfs_root_item, inode, generation, root_dirid, bytenr, byte_limit,
+ bytes_used, last_snapshot, flags, refs, drop_progress,
+ generation_v2, ctransid, otransid, stransid, rtransid, ctime,
+ otime, stime, rtime)
+DEFINE_CONV(btrfs_root_ref, dirid, sequence, name_len)
+DEFINE_CONV(btrfs_file_extent_item, generation, ram_bytes, other_encoding,
+ disk_bytenr, disk_num_bytes, offset, num_bytes)
+DEFINE_CONV_ALT(btrfs_file_extent_item, inl, generation, ram_bytes,
+ other_encoding)
+DEFINE_CONV(btrfs_dev_replace_item, src_devid, cursor_left, cursor_right,
+ cont_reading_from_srcdev_mode, replace_state, time_started,
+ time_stopped, num_write_errors, num_uncorrectable_read_errors)
+DEFINE_CONV(btrfs_block_group_item, used, chunk_objectid, flags)
+DEFINE_CONV(btrfs_free_space_info, extent_count, flags)
+
+DEFINE_CONV(btrfs_header, bytenr, flags, generation, owner, nritems)
+DEFINE_CONV(btrfs_root_backup, tree_root, tree_root_gen, chunk_root,
+ chunk_root_gen, extent_root, extent_root_gen, fs_root, fs_root_gen,
+ dev_root, dev_root_gen, csum_root, csum_root_gen, total_bytes,
+ bytes_used, num_devices)
+DEFINE_CONV(btrfs_super_block, bytenr, flags, magic, generation, root,
+ chunk_root, log_root, log_root_transid, total_bytes, bytes_used,
+ root_dir_objectid, num_devices, sectorsize, nodesize,
+ __unused_leafsize, stripesize, sys_chunk_array_size,
+ chunk_root_generation, compat_flags, compat_ro_flags,
+ incompat_flags, csum_type, dev_item, cache_generation,
+ uuid_tree_generation, super_roots[0], super_roots[1],
+ super_roots[2], super_roots[3])
+DEFINE_CONV(btrfs_item, key, offset, size)
+DEFINE_CONV(btrfs_key_ptr, key, blockptr, generation)
+
+#endif /* __BTRFS_CONV_FUNCS_H__ */
diff --git a/roms/u-boot/fs/btrfs/crypto/hash.c b/roms/u-boot/fs/btrfs/crypto/hash.c
new file mode 100644
index 000000000..fb51f6386
--- /dev/null
+++ b/roms/u-boot/fs/btrfs/crypto/hash.c
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <linux/xxhash.h>
+#include <linux/unaligned/access_ok.h>
+#include <linux/types.h>
+#include <u-boot/sha256.h>
+#include <u-boot/crc.h>
+
+static u32 btrfs_crc32c_table[256];
+
+void btrfs_hash_init(void)
+{
+ static int inited = 0;
+
+ if (!inited) {
+ crc32c_init(btrfs_crc32c_table, 0x82F63B78);
+ inited = 1;
+ }
+}
+
+int hash_sha256(const u8 *buf, size_t length, u8 *out)
+{
+ sha256_context ctx;
+
+ sha256_starts(&ctx);
+ sha256_update(&ctx, buf, length);
+ sha256_finish(&ctx, out);
+
+ return 0;
+}
+
+int hash_xxhash(const u8 *buf, size_t length, u8 *out)
+{
+ u64 hash;
+
+ hash = xxh64(buf, length, 0);
+ put_unaligned_le64(hash, out);
+
+ return 0;
+}
+
+int hash_crc32c(const u8 *buf, size_t length, u8 *out)
+{
+ u32 crc;
+
+ crc = crc32c_cal((u32)~0, (char *)buf, length, btrfs_crc32c_table);
+ put_unaligned_le32(~crc, out);
+
+ return 0;
+}
+
+u32 crc32c(u32 seed, const void * data, size_t len)
+{
+ return crc32c_cal(seed, data, len, btrfs_crc32c_table);
+}
diff --git a/roms/u-boot/fs/btrfs/crypto/hash.h b/roms/u-boot/fs/btrfs/crypto/hash.h
new file mode 100644
index 000000000..d1ba1fa37
--- /dev/null
+++ b/roms/u-boot/fs/btrfs/crypto/hash.h
@@ -0,0 +1,17 @@
+#ifndef CRYPTO_HASH_H
+#define CRYPTO_HASH_H
+
+#include <linux/types.h>
+
+#define CRYPTO_HASH_SIZE_MAX 32
+
+void btrfs_hash_init(void);
+int hash_crc32c(const u8 *buf, size_t length, u8 *out);
+int hash_xxhash(const u8 *buf, size_t length, u8 *out);
+int hash_sha256(const u8 *buf, size_t length, u8 *out);
+
+u32 crc32c(u32 seed, const void * data, size_t len);
+
+/* Blake2B is not yet supported due to lack of library */
+
+#endif
diff --git a/roms/u-boot/fs/btrfs/ctree.c b/roms/u-boot/fs/btrfs/ctree.c
new file mode 100644
index 000000000..5ffced916
--- /dev/null
+++ b/roms/u-boot/fs/btrfs/ctree.c
@@ -0,0 +1,742 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * BTRFS filesystem implementation for U-Boot
+ *
+ * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz
+ */
+
+#include <linux/kernel.h>
+#include <log.h>
+#include <malloc.h>
+#include <memalign.h>
+#include "btrfs.h"
+#include "disk-io.h"
+
+static const struct btrfs_csum {
+ u16 size;
+ const char name[14];
+} btrfs_csums[] = {
+ [BTRFS_CSUM_TYPE_CRC32] = { 4, "crc32c" },
+ [BTRFS_CSUM_TYPE_XXHASH] = { 8, "xxhash64" },
+ [BTRFS_CSUM_TYPE_SHA256] = { 32, "sha256" },
+ [BTRFS_CSUM_TYPE_BLAKE2] = { 32, "blake2" },
+};
+
+u16 btrfs_super_csum_size(const struct btrfs_super_block *sb)
+{
+ const u16 csum_type = btrfs_super_csum_type(sb);
+
+ return btrfs_csums[csum_type].size;
+}
+
+const char *btrfs_super_csum_name(u16 csum_type)
+{
+ return btrfs_csums[csum_type].name;
+}
+
+size_t btrfs_super_num_csums(void)
+{
+ return ARRAY_SIZE(btrfs_csums);
+}
+
+u16 btrfs_csum_type_size(u16 csum_type)
+{
+ return btrfs_csums[csum_type].size;
+}
+
+struct btrfs_path *btrfs_alloc_path(void)
+{
+ struct btrfs_path *path;
+ path = kzalloc(sizeof(struct btrfs_path), GFP_NOFS);
+ return path;
+}
+
+void btrfs_free_path(struct btrfs_path *p)
+{
+ if (!p)
+ return;
+ btrfs_release_path(p);
+ kfree(p);
+}
+
+void btrfs_release_path(struct btrfs_path *p)
+{
+ int i;
+ for (i = 0; i < BTRFS_MAX_LEVEL; i++) {
+ if (!p->nodes[i])
+ continue;
+ free_extent_buffer(p->nodes[i]);
+ }
+ memset(p, 0, sizeof(*p));
+}
+
+int btrfs_comp_cpu_keys(const struct btrfs_key *k1, const struct btrfs_key *k2)
+{
+ if (k1->objectid > k2->objectid)
+ return 1;
+ if (k1->objectid < k2->objectid)
+ return -1;
+ if (k1->type > k2->type)
+ return 1;
+ if (k1->type < k2->type)
+ return -1;
+ if (k1->offset > k2->offset)
+ return 1;
+ if (k1->offset < k2->offset)
+ return -1;
+ return 0;
+}
+
+static int btrfs_comp_keys(struct btrfs_disk_key *disk,
+ const struct btrfs_key *k2)
+{
+ struct btrfs_key k1;
+
+ btrfs_disk_key_to_cpu(&k1, disk);
+ return btrfs_comp_cpu_keys(&k1, k2);
+}
+
+enum btrfs_tree_block_status
+btrfs_check_node(struct btrfs_fs_info *fs_info,
+ struct btrfs_disk_key *parent_key, struct extent_buffer *buf)
+{
+ int i;
+ struct btrfs_key cpukey;
+ struct btrfs_disk_key key;
+ u32 nritems = btrfs_header_nritems(buf);
+ enum btrfs_tree_block_status ret = BTRFS_TREE_BLOCK_INVALID_NRITEMS;
+
+ if (nritems == 0 || nritems > BTRFS_NODEPTRS_PER_BLOCK(fs_info))
+ goto fail;
+
+ ret = BTRFS_TREE_BLOCK_INVALID_PARENT_KEY;
+ if (parent_key && parent_key->type) {
+ btrfs_node_key(buf, &key, 0);
+ if (memcmp(parent_key, &key, sizeof(key)))
+ goto fail;
+ }
+ ret = BTRFS_TREE_BLOCK_BAD_KEY_ORDER;
+ for (i = 0; nritems > 1 && i < nritems - 2; i++) {
+ btrfs_node_key(buf, &key, i);
+ btrfs_node_key_to_cpu(buf, &cpukey, i + 1);
+ if (btrfs_comp_keys(&key, &cpukey) >= 0)
+ goto fail;
+ }
+ return BTRFS_TREE_BLOCK_CLEAN;
+fail:
+ return ret;
+}
+
+enum btrfs_tree_block_status
+btrfs_check_leaf(struct btrfs_fs_info *fs_info,
+ struct btrfs_disk_key *parent_key, struct extent_buffer *buf)
+{
+ int i;
+ struct btrfs_key cpukey;
+ struct btrfs_disk_key key;
+ u32 nritems = btrfs_header_nritems(buf);
+ enum btrfs_tree_block_status ret = BTRFS_TREE_BLOCK_INVALID_NRITEMS;
+
+ if (nritems * sizeof(struct btrfs_item) > buf->len) {
+ fprintf(stderr, "invalid number of items %llu\n",
+ (unsigned long long)buf->start);
+ goto fail;
+ }
+
+ if (btrfs_header_level(buf) != 0) {
+ ret = BTRFS_TREE_BLOCK_INVALID_LEVEL;
+ fprintf(stderr, "leaf is not a leaf %llu\n",
+ (unsigned long long)btrfs_header_bytenr(buf));
+ goto fail;
+ }
+ if (btrfs_leaf_free_space(buf) < 0) {
+ ret = BTRFS_TREE_BLOCK_INVALID_FREE_SPACE;
+ fprintf(stderr, "leaf free space incorrect %llu %d\n",
+ (unsigned long long)btrfs_header_bytenr(buf),
+ btrfs_leaf_free_space(buf));
+ goto fail;
+ }
+
+ if (nritems == 0)
+ return BTRFS_TREE_BLOCK_CLEAN;
+
+ btrfs_item_key(buf, &key, 0);
+ if (parent_key && parent_key->type &&
+ memcmp(parent_key, &key, sizeof(key))) {
+ ret = BTRFS_TREE_BLOCK_INVALID_PARENT_KEY;
+ fprintf(stderr, "leaf parent key incorrect %llu\n",
+ (unsigned long long)btrfs_header_bytenr(buf));
+ goto fail;
+ }
+ for (i = 0; nritems > 1 && i < nritems - 1; i++) {
+ btrfs_item_key(buf, &key, i);
+ btrfs_item_key_to_cpu(buf, &cpukey, i + 1);
+ if (btrfs_comp_keys(&key, &cpukey) >= 0) {
+ ret = BTRFS_TREE_BLOCK_BAD_KEY_ORDER;
+ fprintf(stderr, "bad key ordering %d %d\n", i, i+1);
+ goto fail;
+ }
+ if (btrfs_item_offset_nr(buf, i) !=
+ btrfs_item_end_nr(buf, i + 1)) {
+ ret = BTRFS_TREE_BLOCK_INVALID_OFFSETS;
+ fprintf(stderr, "incorrect offsets %u %u\n",
+ btrfs_item_offset_nr(buf, i),
+ btrfs_item_end_nr(buf, i + 1));
+ goto fail;
+ }
+ if (i == 0 && btrfs_item_end_nr(buf, i) !=
+ BTRFS_LEAF_DATA_SIZE(fs_info)) {
+ ret = BTRFS_TREE_BLOCK_INVALID_OFFSETS;
+ fprintf(stderr, "bad item end %u wanted %u\n",
+ btrfs_item_end_nr(buf, i),
+ (unsigned)BTRFS_LEAF_DATA_SIZE(fs_info));
+ goto fail;
+ }
+ }
+
+ for (i = 0; i < nritems; i++) {
+ if (btrfs_item_end_nr(buf, i) >
+ BTRFS_LEAF_DATA_SIZE(fs_info)) {
+ btrfs_item_key(buf, &key, 0);
+ ret = BTRFS_TREE_BLOCK_INVALID_OFFSETS;
+ fprintf(stderr, "slot end outside of leaf %llu > %llu\n",
+ (unsigned long long)btrfs_item_end_nr(buf, i),
+ (unsigned long long)BTRFS_LEAF_DATA_SIZE(
+ fs_info));
+ goto fail;
+ }
+ }
+
+ return BTRFS_TREE_BLOCK_CLEAN;
+fail:
+ return ret;
+}
+
+static int noinline check_block(struct btrfs_fs_info *fs_info,
+ struct btrfs_path *path, int level)
+{
+ struct btrfs_disk_key key;
+ struct btrfs_disk_key *key_ptr = NULL;
+ struct extent_buffer *parent;
+ enum btrfs_tree_block_status ret;
+
+ if (path->nodes[level + 1]) {
+ parent = path->nodes[level + 1];
+ btrfs_node_key(parent, &key, path->slots[level + 1]);
+ key_ptr = &key;
+ }
+ if (level == 0)
+ ret = btrfs_check_leaf(fs_info, key_ptr, path->nodes[0]);
+ else
+ ret = btrfs_check_node(fs_info, key_ptr, path->nodes[level]);
+ if (ret == BTRFS_TREE_BLOCK_CLEAN)
+ return 0;
+ return -EIO;
+}
+
+/*
+ * search for key in the extent_buffer. The items start at offset p,
+ * and they are item_size apart. There are 'max' items in p.
+ *
+ * the slot in the array is returned via slot, and it points to
+ * the place where you would insert key if it is not found in
+ * the array.
+ *
+ * slot may point to max if the key is bigger than all of the keys
+ */
+static int generic_bin_search(struct extent_buffer *eb, unsigned long p,
+ int item_size, const struct btrfs_key *key,
+ int max, int *slot)
+{
+ int low = 0;
+ int high = max;
+ int mid;
+ int ret;
+ unsigned long offset;
+ struct btrfs_disk_key *tmp;
+
+ while(low < high) {
+ mid = (low + high) / 2;
+ offset = p + mid * item_size;
+
+ tmp = (struct btrfs_disk_key *)(eb->data + offset);
+ ret = btrfs_comp_keys(tmp, key);
+
+ if (ret < 0)
+ low = mid + 1;
+ else if (ret > 0)
+ high = mid;
+ else {
+ *slot = mid;
+ return 0;
+ }
+ }
+ *slot = low;
+ return 1;
+}
+
+/*
+ * simple bin_search frontend that does the right thing for
+ * leaves vs nodes
+ */
+int btrfs_bin_search(struct extent_buffer *eb, const struct btrfs_key *key,
+ int *slot)
+{
+ if (btrfs_header_level(eb) == 0)
+ return generic_bin_search(eb,
+ offsetof(struct btrfs_leaf, items),
+ sizeof(struct btrfs_item),
+ key, btrfs_header_nritems(eb),
+ slot);
+ else
+ return generic_bin_search(eb,
+ offsetof(struct btrfs_node, ptrs),
+ sizeof(struct btrfs_key_ptr),
+ key, btrfs_header_nritems(eb),
+ slot);
+}
+
+struct extent_buffer *read_node_slot(struct btrfs_fs_info *fs_info,
+ struct extent_buffer *parent, int slot)
+{
+ struct extent_buffer *ret;
+ int level = btrfs_header_level(parent);
+
+ if (slot < 0)
+ return NULL;
+ if (slot >= btrfs_header_nritems(parent))
+ return NULL;
+
+ if (level == 0)
+ return NULL;
+
+ ret = read_tree_block(fs_info, btrfs_node_blockptr(parent, slot),
+ btrfs_node_ptr_generation(parent, slot));
+ if (!extent_buffer_uptodate(ret))
+ return ERR_PTR(-EIO);
+
+ if (btrfs_header_level(ret) != level - 1) {
+ error("child eb corrupted: parent bytenr=%llu item=%d parent level=%d child level=%d",
+ btrfs_header_bytenr(parent), slot,
+ btrfs_header_level(parent), btrfs_header_level(ret));
+ free_extent_buffer(ret);
+ return ERR_PTR(-EIO);
+ }
+ return ret;
+}
+
+int btrfs_find_item(struct btrfs_root *fs_root, struct btrfs_path *found_path,
+ u64 iobjectid, u64 ioff, u8 key_type,
+ struct btrfs_key *found_key)
+{
+ int ret;
+ struct btrfs_key key;
+ struct extent_buffer *eb;
+ struct btrfs_path *path;
+
+ key.type = key_type;
+ key.objectid = iobjectid;
+ key.offset = ioff;
+
+ if (found_path == NULL) {
+ path = btrfs_alloc_path();
+ if (!path)
+ return -ENOMEM;
+ } else
+ path = found_path;
+
+ ret = btrfs_search_slot(NULL, fs_root, &key, path, 0, 0);
+ if ((ret < 0) || (found_key == NULL))
+ goto out;
+
+ eb = path->nodes[0];
+ if (ret && path->slots[0] >= btrfs_header_nritems(eb)) {
+ ret = btrfs_next_leaf(fs_root, path);
+ if (ret)
+ goto out;
+ eb = path->nodes[0];
+ }
+
+ btrfs_item_key_to_cpu(eb, found_key, path->slots[0]);
+ if (found_key->type != key.type ||
+ found_key->objectid != key.objectid) {
+ ret = 1;
+ goto out;
+ }
+
+out:
+ if (path != found_path)
+ btrfs_free_path(path);
+ return ret;
+}
+
+/*
+ * look for key in the tree. path is filled in with nodes along the way
+ * if key is found, we return zero and you can find the item in the leaf
+ * level of the path (level 0)
+ *
+ * If the key isn't found, the path points to the slot where it should
+ * be inserted, and 1 is returned. If there are other errors during the
+ * search a negative error number is returned.
+ *
+ * if ins_len > 0, nodes and leaves will be split as we walk down the
+ * tree. if ins_len < 0, nodes will be merged as we walk down the tree (if
+ * possible)
+ *
+ * NOTE: This version has no COW ability, thus we expect trans == NULL,
+ * ins_len == 0 and cow == 0.
+ */
+int btrfs_search_slot(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root, const struct btrfs_key *key,
+ struct btrfs_path *p, int ins_len, int cow)
+{
+ struct extent_buffer *b;
+ int slot;
+ int ret;
+ int level;
+ struct btrfs_fs_info *fs_info = root->fs_info;
+ u8 lowest_level = 0;
+
+ assert(trans == NULL && ins_len == 0 && cow == 0);
+ lowest_level = p->lowest_level;
+ WARN_ON(lowest_level && ins_len > 0);
+ WARN_ON(p->nodes[0] != NULL);
+
+ b = root->node;
+ extent_buffer_get(b);
+ while (b) {
+ level = btrfs_header_level(b);
+ /*
+ if (cow) {
+ int wret;
+ wret = btrfs_cow_block(trans, root, b,
+ p->nodes[level + 1],
+ p->slots[level + 1],
+ &b);
+ if (wret) {
+ free_extent_buffer(b);
+ return wret;
+ }
+ }
+ */
+ BUG_ON(!cow && ins_len);
+ if (level != btrfs_header_level(b))
+ WARN_ON(1);
+ level = btrfs_header_level(b);
+ p->nodes[level] = b;
+ ret = check_block(fs_info, p, level);
+ if (ret)
+ return -1;
+ ret = btrfs_bin_search(b, key, &slot);
+ if (level != 0) {
+ if (ret && slot > 0)
+ slot -= 1;
+ p->slots[level] = slot;
+ /*
+ if ((p->search_for_split || ins_len > 0) &&
+ btrfs_header_nritems(b) >=
+ BTRFS_NODEPTRS_PER_BLOCK(fs_info) - 3) {
+ int sret = split_node(trans, root, p, level);
+ BUG_ON(sret > 0);
+ if (sret)
+ return sret;
+ b = p->nodes[level];
+ slot = p->slots[level];
+ } else if (ins_len < 0) {
+ int sret = balance_level(trans, root, p,
+ level);
+ if (sret)
+ return sret;
+ b = p->nodes[level];
+ if (!b) {
+ btrfs_release_path(p);
+ goto again;
+ }
+ slot = p->slots[level];
+ BUG_ON(btrfs_header_nritems(b) == 1);
+ }
+ */
+ /* this is only true while dropping a snapshot */
+ if (level == lowest_level)
+ break;
+
+ b = read_node_slot(fs_info, b, slot);
+ if (!extent_buffer_uptodate(b))
+ return -EIO;
+ } else {
+ p->slots[level] = slot;
+ /*
+ if (ins_len > 0 &&
+ ins_len > btrfs_leaf_free_space(b)) {
+ int sret = split_leaf(trans, root, key,
+ p, ins_len, ret == 0);
+ BUG_ON(sret > 0);
+ if (sret)
+ return sret;
+ }
+ */
+ return ret;
+ }
+ }
+ return 1;
+}
+
+/*
+ * Helper to use instead of search slot if no exact match is needed but
+ * instead the next or previous item should be returned.
+ * When find_higher is true, the next higher item is returned, the next lower
+ * otherwise.
+ * When return_any and find_higher are both true, and no higher item is found,
+ * return the next lower instead.
+ * When return_any is true and find_higher is false, and no lower item is found,
+ * return the next higher instead.
+ * It returns 0 if any item is found, 1 if none is found (tree empty), and
+ * < 0 on error
+ */
+int btrfs_search_slot_for_read(struct btrfs_root *root,
+ const struct btrfs_key *key,
+ struct btrfs_path *p, int find_higher,
+ int return_any)
+{
+ int ret;
+ struct extent_buffer *leaf;
+
+again:
+ ret = btrfs_search_slot(NULL, root, key, p, 0, 0);
+ if (ret <= 0)
+ return ret;
+ /*
+ * A return value of 1 means the path is at the position where the item
+ * should be inserted. Normally this is the next bigger item, but in
+ * case the previous item is the last in a leaf, path points to the
+ * first free slot in the previous leaf, i.e. at an invalid item.
+ */
+ leaf = p->nodes[0];
+
+ if (find_higher) {
+ if (p->slots[0] >= btrfs_header_nritems(leaf)) {
+ ret = btrfs_next_leaf(root, p);
+ if (ret <= 0)
+ return ret;
+ if (!return_any)
+ return 1;
+ /*
+ * No higher item found, return the next lower instead
+ */
+ return_any = 0;
+ find_higher = 0;
+ btrfs_release_path(p);
+ goto again;
+ }
+ } else {
+ if (p->slots[0] == 0) {
+ ret = btrfs_prev_leaf(root, p);
+ if (ret < 0)
+ return ret;
+ if (!ret) {
+ leaf = p->nodes[0];
+ if (p->slots[0] == btrfs_header_nritems(leaf))
+ p->slots[0]--;
+ return 0;
+ }
+ if (!return_any)
+ return 1;
+ /*
+ * No lower item found, return the next higher instead
+ */
+ return_any = 0;
+ find_higher = 1;
+ btrfs_release_path(p);
+ goto again;
+ } else {
+ --p->slots[0];
+ }
+ }
+ return 0;
+}
+
+/*
+ * how many bytes are required to store the items in a leaf. start
+ * and nr indicate which items in the leaf to check. This totals up the
+ * space used both by the item structs and the item data
+ */
+static int leaf_space_used(struct extent_buffer *l, int start, int nr)
+{
+ int data_len;
+ int nritems = btrfs_header_nritems(l);
+ int end = min(nritems, start + nr) - 1;
+
+ if (!nr)
+ return 0;
+ data_len = btrfs_item_end_nr(l, start);
+ data_len = data_len - btrfs_item_offset_nr(l, end);
+ data_len += sizeof(struct btrfs_item) * nr;
+ WARN_ON(data_len < 0);
+ return data_len;
+}
+
+/*
+ * The space between the end of the leaf items and
+ * the start of the leaf data. IOW, how much room
+ * the leaf has left for both items and data
+ */
+int btrfs_leaf_free_space(struct extent_buffer *leaf)
+{
+ int nritems = btrfs_header_nritems(leaf);
+ u32 leaf_data_size;
+ int ret;
+
+ BUG_ON(leaf->fs_info && leaf->fs_info->nodesize != leaf->len);
+ leaf_data_size = __BTRFS_LEAF_DATA_SIZE(leaf->len);
+ ret = leaf_data_size - leaf_space_used(leaf, 0 ,nritems);
+ if (ret < 0) {
+ printk("leaf free space ret %d, leaf data size %u, used %d nritems %d\n",
+ ret, leaf_data_size, leaf_space_used(leaf, 0, nritems),
+ nritems);
+ }
+ return ret;
+}
+
+/*
+ * walk up the tree as far as required to find the previous leaf.
+ * returns 0 if it found something or 1 if there are no lesser leaves.
+ * returns < 0 on io errors.
+ */
+int btrfs_prev_leaf(struct btrfs_root *root, struct btrfs_path *path)
+{
+ int slot;
+ int level = 1;
+ struct extent_buffer *c;
+ struct extent_buffer *next = NULL;
+ struct btrfs_fs_info *fs_info = root->fs_info;
+
+ while(level < BTRFS_MAX_LEVEL) {
+ if (!path->nodes[level])
+ return 1;
+
+ slot = path->slots[level];
+ c = path->nodes[level];
+ if (slot == 0) {
+ level++;
+ if (level == BTRFS_MAX_LEVEL)
+ return 1;
+ continue;
+ }
+ slot--;
+
+ next = read_node_slot(fs_info, c, slot);
+ if (!extent_buffer_uptodate(next)) {
+ if (IS_ERR(next))
+ return PTR_ERR(next);
+ return -EIO;
+ }
+ break;
+ }
+ path->slots[level] = slot;
+ while(1) {
+ level--;
+ c = path->nodes[level];
+ free_extent_buffer(c);
+ slot = btrfs_header_nritems(next);
+ if (slot != 0)
+ slot--;
+ path->nodes[level] = next;
+ path->slots[level] = slot;
+ if (!level)
+ break;
+ next = read_node_slot(fs_info, next, slot);
+ if (!extent_buffer_uptodate(next)) {
+ if (IS_ERR(next))
+ return PTR_ERR(next);
+ return -EIO;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Walk up the tree as far as necessary to find the next sibling tree block.
+ * More generic version of btrfs_next_leaf(), as it could find sibling nodes
+ * if @path->lowest_level is not 0.
+ *
+ * returns 0 if it found something or 1 if there are no greater leaves.
+ * returns < 0 on io errors.
+ */
+int btrfs_next_sibling_tree_block(struct btrfs_fs_info *fs_info,
+ struct btrfs_path *path)
+{
+ int slot;
+ int level = path->lowest_level + 1;
+ struct extent_buffer *c;
+ struct extent_buffer *next = NULL;
+
+ BUG_ON(path->lowest_level + 1 >= BTRFS_MAX_LEVEL);
+ do {
+ if (!path->nodes[level])
+ return 1;
+
+ slot = path->slots[level] + 1;
+ c = path->nodes[level];
+ if (slot >= btrfs_header_nritems(c)) {
+ level++;
+ if (level == BTRFS_MAX_LEVEL)
+ return 1;
+ continue;
+ }
+
+ next = read_node_slot(fs_info, c, slot);
+ if (!extent_buffer_uptodate(next))
+ return -EIO;
+ break;
+ } while (level < BTRFS_MAX_LEVEL);
+ path->slots[level] = slot;
+ while(1) {
+ level--;
+ c = path->nodes[level];
+ free_extent_buffer(c);
+ path->nodes[level] = next;
+ path->slots[level] = 0;
+ if (level == path->lowest_level)
+ break;
+ next = read_node_slot(fs_info, next, 0);
+ if (!extent_buffer_uptodate(next))
+ return -EIO;
+ }
+ return 0;
+}
+
+int btrfs_previous_item(struct btrfs_root *root,
+ struct btrfs_path *path, u64 min_objectid,
+ int type)
+{
+ struct btrfs_key found_key;
+ struct extent_buffer *leaf;
+ u32 nritems;
+ int ret;
+
+ while(1) {
+ if (path->slots[0] == 0) {
+ ret = btrfs_prev_leaf(root, path);
+ if (ret != 0)
+ return ret;
+ } else {
+ path->slots[0]--;
+ }
+ leaf = path->nodes[0];
+ nritems = btrfs_header_nritems(leaf);
+ if (nritems == 0)
+ return 1;
+ if (path->slots[0] == nritems)
+ path->slots[0]--;
+
+ btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
+ if (found_key.objectid < min_objectid)
+ break;
+ if (found_key.type == type)
+ return 0;
+ if (found_key.objectid == min_objectid &&
+ found_key.type < type)
+ break;
+ }
+ return 1;
+}
diff --git a/roms/u-boot/fs/btrfs/ctree.h b/roms/u-boot/fs/btrfs/ctree.h
new file mode 100644
index 000000000..219c410b1
--- /dev/null
+++ b/roms/u-boot/fs/btrfs/ctree.h
@@ -0,0 +1,1298 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * From linux/fs/btrfs/ctree.h
+ * Copyright (C) 2007,2008 Oracle. All rights reserved.
+ *
+ * Modified in 2017 by Marek Behun, CZ.NIC, marek.behun@nic.cz
+ */
+
+#ifndef __BTRFS_CTREE_H__
+#define __BTRFS_CTREE_H__
+
+#include <common.h>
+#include <compiler.h>
+#include <linux/rbtree.h>
+#include <linux/bug.h>
+#include <linux/unaligned/le_byteshift.h>
+#include <u-boot/crc.h>
+#include "kernel-shared/btrfs_tree.h"
+#include "crypto/hash.h"
+#include "compat.h"
+#include "extent-io.h"
+
+#define BTRFS_MAX_MIRRORS 3
+
+/*
+ * the max metadata block size. This limit is somewhat artificial,
+ * but the memmove costs go through the roof for larger blocks.
+ */
+#define BTRFS_MAX_METADATA_BLOCKSIZE 65536
+
+/*
+ * Theoretical limit is larger, but we keep this down to a sane
+ * value. That should limit greatly the possibility of collisions on
+ * inode ref items.
+ */
+#define BTRFS_LINK_MAX 65535U
+
+/* four bytes for CRC32 */
+#define BTRFS_EMPTY_DIR_SIZE 0
+
+struct btrfs_mapping_tree {
+ struct cache_tree cache_tree;
+};
+
+static inline unsigned long btrfs_chunk_item_size(int num_stripes)
+{
+ BUG_ON(num_stripes == 0);
+ return sizeof(struct btrfs_chunk) +
+ sizeof(struct btrfs_stripe) * (num_stripes - 1);
+}
+
+#define __BTRFS_LEAF_DATA_SIZE(bs) ((bs) - sizeof(struct btrfs_header))
+#define BTRFS_LEAF_DATA_SIZE(fs_info) \
+ (__BTRFS_LEAF_DATA_SIZE(fs_info->nodesize))
+
+struct btrfs_path {
+ struct extent_buffer *nodes[BTRFS_MAX_LEVEL];
+ int slots[BTRFS_MAX_LEVEL];
+
+ /* keep some upper locks as we walk down */
+ u8 lowest_level;
+};
+
+/* ioprio of readahead is set to idle */
+#define BTRFS_IOPRIO_READA (IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0))
+
+#define BTRFS_DIRTY_METADATA_THRESH SZ_32M
+
+#define BTRFS_MAX_EXTENT_SIZE SZ_128M
+
+enum btrfs_tree_block_status {
+ BTRFS_TREE_BLOCK_CLEAN,
+ BTRFS_TREE_BLOCK_INVALID_NRITEMS,
+ BTRFS_TREE_BLOCK_INVALID_PARENT_KEY,
+ BTRFS_TREE_BLOCK_BAD_KEY_ORDER,
+ BTRFS_TREE_BLOCK_INVALID_LEVEL,
+ BTRFS_TREE_BLOCK_INVALID_FREE_SPACE,
+ BTRFS_TREE_BLOCK_INVALID_OFFSETS,
+};
+
+struct btrfs_root {
+ struct extent_buffer *node;
+ struct btrfs_root_item root_item;
+ struct btrfs_key root_key;
+ struct btrfs_fs_info *fs_info;
+ u64 objectid;
+ u64 last_trans;
+
+ int ref_cows;
+ int track_dirty;
+
+ u32 type;
+ u64 last_inode_alloc;
+
+ struct rb_node rb_node;
+};
+
+struct btrfs_trans_handle;
+struct btrfs_device;
+struct btrfs_fs_devices;
+struct btrfs_fs_info {
+ u8 chunk_tree_uuid[BTRFS_UUID_SIZE];
+ u8 *new_chunk_tree_uuid;
+ struct btrfs_root *fs_root;
+ struct btrfs_root *tree_root;
+ struct btrfs_root *chunk_root;
+ struct btrfs_root *csum_root;
+
+ struct rb_root fs_root_tree;
+
+ struct extent_io_tree extent_cache;
+
+ /* logical->physical extent mapping */
+ struct btrfs_mapping_tree mapping_tree;
+
+ u64 last_trans_committed;
+
+ struct btrfs_super_block *super_copy;
+
+ struct btrfs_fs_devices *fs_devices;
+
+ /* Cached block sizes */
+ u32 nodesize;
+ u32 sectorsize;
+ u32 stripesize;
+};
+
+static inline u32 BTRFS_MAX_ITEM_SIZE(const struct btrfs_fs_info *info)
+{
+ return BTRFS_LEAF_DATA_SIZE(info) - sizeof(struct btrfs_item);
+}
+
+static inline u32 BTRFS_NODEPTRS_PER_BLOCK(const struct btrfs_fs_info *info)
+{
+ return BTRFS_LEAF_DATA_SIZE(info) / sizeof(struct btrfs_key_ptr);
+}
+
+static inline u32 BTRFS_NODEPTRS_PER_EXTENT_BUFFER(const struct extent_buffer *eb)
+{
+ BUG_ON(eb->fs_info && eb->fs_info->nodesize != eb->len);
+ return __BTRFS_LEAF_DATA_SIZE(eb->len) / sizeof(struct btrfs_key_ptr);
+}
+
+#define BTRFS_FILE_EXTENT_INLINE_DATA_START \
+ (offsetof(struct btrfs_file_extent_item, disk_bytenr))
+static inline u32 BTRFS_MAX_INLINE_DATA_SIZE(const struct btrfs_fs_info *info)
+{
+ return BTRFS_MAX_ITEM_SIZE(info) -
+ BTRFS_FILE_EXTENT_INLINE_DATA_START;
+}
+
+static inline u32 BTRFS_MAX_XATTR_SIZE(const struct btrfs_fs_info *info)
+{
+ return BTRFS_MAX_ITEM_SIZE(info) - sizeof(struct btrfs_dir_item);
+}
+
+/*
+ * File system states
+ */
+#define BTRFS_FS_STATE_ERROR 0
+#define BTRFS_FS_STATE_REMOUNTING 1
+#define BTRFS_FS_STATE_TRANS_ABORTED 2
+#define BTRFS_FS_STATE_DEV_REPLACING 3
+#define BTRFS_FS_STATE_DUMMY_FS_INFO 4
+
+#define read_eb_member(eb, ptr, type, member, result) ( \
+ read_extent_buffer(eb, (char *)(result), \
+ ((unsigned long)(ptr)) + \
+ offsetof(type, member), \
+ sizeof(((type *)0)->member)))
+
+#define write_eb_member(eb, ptr, type, member, result) ( \
+ write_extent_buffer(eb, (char *)(result), \
+ ((unsigned long)(ptr)) + \
+ offsetof(type, member), \
+ sizeof(((type *)0)->member)))
+
+#define BTRFS_SETGET_HEADER_FUNCS(name, type, member, bits) \
+static inline u##bits btrfs_##name(const struct extent_buffer *eb) \
+{ \
+ const struct btrfs_header *h = (struct btrfs_header *)eb->data; \
+ return le##bits##_to_cpu(h->member); \
+} \
+static inline void btrfs_set_##name(struct extent_buffer *eb, \
+ u##bits val) \
+{ \
+ struct btrfs_header *h = (struct btrfs_header *)eb->data; \
+ h->member = cpu_to_le##bits(val); \
+}
+
+#define BTRFS_SETGET_FUNCS(name, type, member, bits) \
+static inline u##bits btrfs_##name(const struct extent_buffer *eb, \
+ const type *s) \
+{ \
+ unsigned long offset = (unsigned long)s; \
+ const type *p = (type *) (eb->data + offset); \
+ return get_unaligned_le##bits(&p->member); \
+} \
+static inline void btrfs_set_##name(struct extent_buffer *eb, \
+ type *s, u##bits val) \
+{ \
+ unsigned long offset = (unsigned long)s; \
+ type *p = (type *) (eb->data + offset); \
+ put_unaligned_le##bits(val, &p->member); \
+}
+
+#define BTRFS_SETGET_STACK_FUNCS(name, type, member, bits) \
+static inline u##bits btrfs_##name(const type *s) \
+{ \
+ return le##bits##_to_cpu(s->member); \
+} \
+static inline void btrfs_set_##name(type *s, u##bits val) \
+{ \
+ s->member = cpu_to_le##bits(val); \
+}
+
+BTRFS_SETGET_FUNCS(device_type, struct btrfs_dev_item, type, 64);
+BTRFS_SETGET_FUNCS(device_total_bytes, struct btrfs_dev_item, total_bytes, 64);
+BTRFS_SETGET_FUNCS(device_bytes_used, struct btrfs_dev_item, bytes_used, 64);
+BTRFS_SETGET_FUNCS(device_io_align, struct btrfs_dev_item, io_align, 32);
+BTRFS_SETGET_FUNCS(device_io_width, struct btrfs_dev_item, io_width, 32);
+BTRFS_SETGET_FUNCS(device_start_offset, struct btrfs_dev_item,
+ start_offset, 64);
+BTRFS_SETGET_FUNCS(device_sector_size, struct btrfs_dev_item, sector_size, 32);
+BTRFS_SETGET_FUNCS(device_id, struct btrfs_dev_item, devid, 64);
+BTRFS_SETGET_FUNCS(device_group, struct btrfs_dev_item, dev_group, 32);
+BTRFS_SETGET_FUNCS(device_seek_speed, struct btrfs_dev_item, seek_speed, 8);
+BTRFS_SETGET_FUNCS(device_bandwidth, struct btrfs_dev_item, bandwidth, 8);
+BTRFS_SETGET_FUNCS(device_generation, struct btrfs_dev_item, generation, 64);
+
+BTRFS_SETGET_STACK_FUNCS(stack_device_type, struct btrfs_dev_item, type, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_device_total_bytes, struct btrfs_dev_item,
+ total_bytes, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_device_bytes_used, struct btrfs_dev_item,
+ bytes_used, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_device_io_align, struct btrfs_dev_item,
+ io_align, 32);
+BTRFS_SETGET_STACK_FUNCS(stack_device_io_width, struct btrfs_dev_item,
+ io_width, 32);
+BTRFS_SETGET_STACK_FUNCS(stack_device_sector_size, struct btrfs_dev_item,
+ sector_size, 32);
+BTRFS_SETGET_STACK_FUNCS(stack_device_id, struct btrfs_dev_item, devid, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_device_group, struct btrfs_dev_item,
+ dev_group, 32);
+BTRFS_SETGET_STACK_FUNCS(stack_device_seek_speed, struct btrfs_dev_item,
+ seek_speed, 8);
+BTRFS_SETGET_STACK_FUNCS(stack_device_bandwidth, struct btrfs_dev_item,
+ bandwidth, 8);
+BTRFS_SETGET_STACK_FUNCS(stack_device_generation, struct btrfs_dev_item,
+ generation, 64);
+
+static inline char *btrfs_device_uuid(struct btrfs_dev_item *d)
+{
+ return (char *)d + offsetof(struct btrfs_dev_item, uuid);
+}
+
+static inline char *btrfs_device_fsid(struct btrfs_dev_item *d)
+{
+ return (char *)d + offsetof(struct btrfs_dev_item, fsid);
+}
+
+BTRFS_SETGET_FUNCS(chunk_length, struct btrfs_chunk, length, 64);
+BTRFS_SETGET_FUNCS(chunk_owner, struct btrfs_chunk, owner, 64);
+BTRFS_SETGET_FUNCS(chunk_stripe_len, struct btrfs_chunk, stripe_len, 64);
+BTRFS_SETGET_FUNCS(chunk_io_align, struct btrfs_chunk, io_align, 32);
+BTRFS_SETGET_FUNCS(chunk_io_width, struct btrfs_chunk, io_width, 32);
+BTRFS_SETGET_FUNCS(chunk_sector_size, struct btrfs_chunk, sector_size, 32);
+BTRFS_SETGET_FUNCS(chunk_type, struct btrfs_chunk, type, 64);
+BTRFS_SETGET_FUNCS(chunk_num_stripes, struct btrfs_chunk, num_stripes, 16);
+BTRFS_SETGET_FUNCS(chunk_sub_stripes, struct btrfs_chunk, sub_stripes, 16);
+BTRFS_SETGET_FUNCS(stripe_devid, struct btrfs_stripe, devid, 64);
+BTRFS_SETGET_FUNCS(stripe_offset, struct btrfs_stripe, offset, 64);
+
+static inline char *btrfs_stripe_dev_uuid(struct btrfs_stripe *s)
+{
+ return (char *)s + offsetof(struct btrfs_stripe, dev_uuid);
+}
+
+BTRFS_SETGET_STACK_FUNCS(stack_chunk_length, struct btrfs_chunk, length, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_chunk_owner, struct btrfs_chunk, owner, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_chunk_stripe_len, struct btrfs_chunk,
+ stripe_len, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_chunk_io_align, struct btrfs_chunk,
+ io_align, 32);
+BTRFS_SETGET_STACK_FUNCS(stack_chunk_io_width, struct btrfs_chunk,
+ io_width, 32);
+BTRFS_SETGET_STACK_FUNCS(stack_chunk_sector_size, struct btrfs_chunk,
+ sector_size, 32);
+BTRFS_SETGET_STACK_FUNCS(stack_chunk_type, struct btrfs_chunk, type, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_chunk_num_stripes, struct btrfs_chunk,
+ num_stripes, 16);
+BTRFS_SETGET_STACK_FUNCS(stack_chunk_sub_stripes, struct btrfs_chunk,
+ sub_stripes, 16);
+BTRFS_SETGET_STACK_FUNCS(stack_stripe_devid, struct btrfs_stripe, devid, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_stripe_offset, struct btrfs_stripe, offset, 64);
+
+static inline struct btrfs_stripe *btrfs_stripe_nr(struct btrfs_chunk *c,
+ int nr)
+{
+ unsigned long offset = (unsigned long)c;
+ offset += offsetof(struct btrfs_chunk, stripe);
+ offset += nr * sizeof(struct btrfs_stripe);
+ return (struct btrfs_stripe *)offset;
+}
+
+static inline char *btrfs_stripe_dev_uuid_nr(struct btrfs_chunk *c, int nr)
+{
+ return btrfs_stripe_dev_uuid(btrfs_stripe_nr(c, nr));
+}
+
+static inline u64 btrfs_stripe_offset_nr(struct extent_buffer *eb,
+ struct btrfs_chunk *c, int nr)
+{
+ return btrfs_stripe_offset(eb, btrfs_stripe_nr(c, nr));
+}
+
+static inline void btrfs_set_stripe_offset_nr(struct extent_buffer *eb,
+ struct btrfs_chunk *c, int nr,
+ u64 val)
+{
+ btrfs_set_stripe_offset(eb, btrfs_stripe_nr(c, nr), val);
+}
+
+static inline u64 btrfs_stripe_devid_nr(struct extent_buffer *eb,
+ struct btrfs_chunk *c, int nr)
+{
+ return btrfs_stripe_devid(eb, btrfs_stripe_nr(c, nr));
+}
+
+static inline void btrfs_set_stripe_devid_nr(struct extent_buffer *eb,
+ struct btrfs_chunk *c, int nr,
+ u64 val)
+{
+ btrfs_set_stripe_devid(eb, btrfs_stripe_nr(c, nr), val);
+}
+
+/* struct btrfs_block_group_item */
+BTRFS_SETGET_STACK_FUNCS(block_group_used, struct btrfs_block_group_item,
+ used, 64);
+BTRFS_SETGET_FUNCS(disk_block_group_used, struct btrfs_block_group_item,
+ used, 64);
+BTRFS_SETGET_STACK_FUNCS(block_group_chunk_objectid,
+ struct btrfs_block_group_item, chunk_objectid, 64);
+
+BTRFS_SETGET_FUNCS(disk_block_group_chunk_objectid,
+ struct btrfs_block_group_item, chunk_objectid, 64);
+BTRFS_SETGET_FUNCS(disk_block_group_flags,
+ struct btrfs_block_group_item, flags, 64);
+BTRFS_SETGET_STACK_FUNCS(block_group_flags,
+ struct btrfs_block_group_item, flags, 64);
+
+/* struct btrfs_free_space_info */
+BTRFS_SETGET_FUNCS(free_space_extent_count, struct btrfs_free_space_info,
+ extent_count, 32);
+BTRFS_SETGET_FUNCS(free_space_flags, struct btrfs_free_space_info, flags, 32);
+
+/* struct btrfs_inode_ref */
+BTRFS_SETGET_FUNCS(inode_ref_name_len, struct btrfs_inode_ref, name_len, 16);
+BTRFS_SETGET_STACK_FUNCS(stack_inode_ref_name_len, struct btrfs_inode_ref, name_len, 16);
+BTRFS_SETGET_FUNCS(inode_ref_index, struct btrfs_inode_ref, index, 64);
+
+/* struct btrfs_inode_extref */
+BTRFS_SETGET_FUNCS(inode_extref_parent, struct btrfs_inode_extref,
+ parent_objectid, 64);
+BTRFS_SETGET_FUNCS(inode_extref_name_len, struct btrfs_inode_extref,
+ name_len, 16);
+BTRFS_SETGET_FUNCS(inode_extref_index, struct btrfs_inode_extref, index, 64);
+
+/* struct btrfs_inode_item */
+BTRFS_SETGET_FUNCS(inode_generation, struct btrfs_inode_item, generation, 64);
+BTRFS_SETGET_FUNCS(inode_sequence, struct btrfs_inode_item, sequence, 64);
+BTRFS_SETGET_FUNCS(inode_transid, struct btrfs_inode_item, transid, 64);
+BTRFS_SETGET_FUNCS(inode_size, struct btrfs_inode_item, size, 64);
+BTRFS_SETGET_FUNCS(inode_nbytes, struct btrfs_inode_item, nbytes, 64);
+BTRFS_SETGET_FUNCS(inode_block_group, struct btrfs_inode_item, block_group, 64);
+BTRFS_SETGET_FUNCS(inode_nlink, struct btrfs_inode_item, nlink, 32);
+BTRFS_SETGET_FUNCS(inode_uid, struct btrfs_inode_item, uid, 32);
+BTRFS_SETGET_FUNCS(inode_gid, struct btrfs_inode_item, gid, 32);
+BTRFS_SETGET_FUNCS(inode_mode, struct btrfs_inode_item, mode, 32);
+BTRFS_SETGET_FUNCS(inode_rdev, struct btrfs_inode_item, rdev, 64);
+BTRFS_SETGET_FUNCS(inode_flags, struct btrfs_inode_item, flags, 64);
+
+BTRFS_SETGET_STACK_FUNCS(stack_inode_generation,
+ struct btrfs_inode_item, generation, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_inode_sequence,
+ struct btrfs_inode_item, sequence, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_inode_transid,
+ struct btrfs_inode_item, transid, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_inode_size,
+ struct btrfs_inode_item, size, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_inode_nbytes,
+ struct btrfs_inode_item, nbytes, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_inode_block_group,
+ struct btrfs_inode_item, block_group, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_inode_nlink,
+ struct btrfs_inode_item, nlink, 32);
+BTRFS_SETGET_STACK_FUNCS(stack_inode_uid,
+ struct btrfs_inode_item, uid, 32);
+BTRFS_SETGET_STACK_FUNCS(stack_inode_gid,
+ struct btrfs_inode_item, gid, 32);
+BTRFS_SETGET_STACK_FUNCS(stack_inode_mode,
+ struct btrfs_inode_item, mode, 32);
+BTRFS_SETGET_STACK_FUNCS(stack_inode_rdev,
+ struct btrfs_inode_item, rdev, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_inode_flags,
+ struct btrfs_inode_item, flags, 64);
+
+static inline struct btrfs_timespec *
+btrfs_inode_atime(struct btrfs_inode_item *inode_item)
+{
+ unsigned long ptr = (unsigned long)inode_item;
+ ptr += offsetof(struct btrfs_inode_item, atime);
+ return (struct btrfs_timespec *)ptr;
+}
+
+static inline struct btrfs_timespec *
+btrfs_inode_mtime(struct btrfs_inode_item *inode_item)
+{
+ unsigned long ptr = (unsigned long)inode_item;
+ ptr += offsetof(struct btrfs_inode_item, mtime);
+ return (struct btrfs_timespec *)ptr;
+}
+
+static inline struct btrfs_timespec *
+btrfs_inode_ctime(struct btrfs_inode_item *inode_item)
+{
+ unsigned long ptr = (unsigned long)inode_item;
+ ptr += offsetof(struct btrfs_inode_item, ctime);
+ return (struct btrfs_timespec *)ptr;
+}
+
+static inline struct btrfs_timespec *
+btrfs_inode_otime(struct btrfs_inode_item *inode_item)
+{
+ unsigned long ptr = (unsigned long)inode_item;
+ ptr += offsetof(struct btrfs_inode_item, otime);
+ return (struct btrfs_timespec *)ptr;
+}
+
+BTRFS_SETGET_FUNCS(timespec_sec, struct btrfs_timespec, sec, 64);
+BTRFS_SETGET_FUNCS(timespec_nsec, struct btrfs_timespec, nsec, 32);
+BTRFS_SETGET_STACK_FUNCS(stack_timespec_sec, struct btrfs_timespec,
+ sec, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_timespec_nsec, struct btrfs_timespec,
+ nsec, 32);
+
+/* struct btrfs_dev_extent */
+BTRFS_SETGET_FUNCS(dev_extent_chunk_tree, struct btrfs_dev_extent,
+ chunk_tree, 64);
+BTRFS_SETGET_FUNCS(dev_extent_chunk_objectid, struct btrfs_dev_extent,
+ chunk_objectid, 64);
+BTRFS_SETGET_FUNCS(dev_extent_chunk_offset, struct btrfs_dev_extent,
+ chunk_offset, 64);
+BTRFS_SETGET_FUNCS(dev_extent_length, struct btrfs_dev_extent, length, 64);
+
+BTRFS_SETGET_STACK_FUNCS(stack_dev_extent_length, struct btrfs_dev_extent,
+ length, 64);
+
+static inline u8 *btrfs_dev_extent_chunk_tree_uuid(struct btrfs_dev_extent *dev)
+{
+ unsigned long ptr = offsetof(struct btrfs_dev_extent, chunk_tree_uuid);
+ return (u8 *)((unsigned long)dev + ptr);
+}
+
+
+/* struct btrfs_extent_item */
+BTRFS_SETGET_FUNCS(extent_refs, struct btrfs_extent_item, refs, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_extent_refs, struct btrfs_extent_item, refs, 64);
+BTRFS_SETGET_FUNCS(extent_generation, struct btrfs_extent_item,
+ generation, 64);
+BTRFS_SETGET_FUNCS(extent_flags, struct btrfs_extent_item, flags, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_extent_flags, struct btrfs_extent_item, flags, 64);
+
+BTRFS_SETGET_FUNCS(extent_refs_v0, struct btrfs_extent_item_v0, refs, 32);
+
+BTRFS_SETGET_FUNCS(tree_block_level, struct btrfs_tree_block_info, level, 8);
+
+static inline void btrfs_tree_block_key(struct extent_buffer *eb,
+ struct btrfs_tree_block_info *item,
+ struct btrfs_disk_key *key)
+{
+ read_eb_member(eb, item, struct btrfs_tree_block_info, key, key);
+}
+
+static inline void btrfs_set_tree_block_key(struct extent_buffer *eb,
+ struct btrfs_tree_block_info *item,
+ struct btrfs_disk_key *key)
+{
+ write_eb_member(eb, item, struct btrfs_tree_block_info, key, key);
+}
+
+BTRFS_SETGET_FUNCS(extent_data_ref_root, struct btrfs_extent_data_ref,
+ root, 64);
+BTRFS_SETGET_FUNCS(extent_data_ref_objectid, struct btrfs_extent_data_ref,
+ objectid, 64);
+BTRFS_SETGET_FUNCS(extent_data_ref_offset, struct btrfs_extent_data_ref,
+ offset, 64);
+BTRFS_SETGET_FUNCS(extent_data_ref_count, struct btrfs_extent_data_ref,
+ count, 32);
+
+BTRFS_SETGET_FUNCS(shared_data_ref_count, struct btrfs_shared_data_ref,
+ count, 32);
+
+BTRFS_SETGET_FUNCS(extent_inline_ref_type, struct btrfs_extent_inline_ref,
+ type, 8);
+BTRFS_SETGET_FUNCS(extent_inline_ref_offset, struct btrfs_extent_inline_ref,
+ offset, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_extent_inline_ref_type,
+ struct btrfs_extent_inline_ref, type, 8);
+BTRFS_SETGET_STACK_FUNCS(stack_extent_inline_ref_offset,
+ struct btrfs_extent_inline_ref, offset, 64);
+
+static inline u32 btrfs_extent_inline_ref_size(int type)
+{
+ if (type == BTRFS_TREE_BLOCK_REF_KEY ||
+ type == BTRFS_SHARED_BLOCK_REF_KEY)
+ return sizeof(struct btrfs_extent_inline_ref);
+ if (type == BTRFS_SHARED_DATA_REF_KEY)
+ return sizeof(struct btrfs_shared_data_ref) +
+ sizeof(struct btrfs_extent_inline_ref);
+ if (type == BTRFS_EXTENT_DATA_REF_KEY)
+ return sizeof(struct btrfs_extent_data_ref) +
+ offsetof(struct btrfs_extent_inline_ref, offset);
+ BUG();
+ return 0;
+}
+
+BTRFS_SETGET_FUNCS(ref_root_v0, struct btrfs_extent_ref_v0, root, 64);
+BTRFS_SETGET_FUNCS(ref_generation_v0, struct btrfs_extent_ref_v0,
+ generation, 64);
+BTRFS_SETGET_FUNCS(ref_objectid_v0, struct btrfs_extent_ref_v0, objectid, 64);
+BTRFS_SETGET_FUNCS(ref_count_v0, struct btrfs_extent_ref_v0, count, 32);
+
+/* struct btrfs_node */
+BTRFS_SETGET_FUNCS(key_blockptr, struct btrfs_key_ptr, blockptr, 64);
+BTRFS_SETGET_FUNCS(key_generation, struct btrfs_key_ptr, generation, 64);
+
+static inline u64 btrfs_node_blockptr(struct extent_buffer *eb, int nr)
+{
+ unsigned long ptr;
+ ptr = offsetof(struct btrfs_node, ptrs) +
+ sizeof(struct btrfs_key_ptr) * nr;
+ return btrfs_key_blockptr(eb, (struct btrfs_key_ptr *)ptr);
+}
+
+static inline void btrfs_set_node_blockptr(struct extent_buffer *eb,
+ int nr, u64 val)
+{
+ unsigned long ptr;
+ ptr = offsetof(struct btrfs_node, ptrs) +
+ sizeof(struct btrfs_key_ptr) * nr;
+ btrfs_set_key_blockptr(eb, (struct btrfs_key_ptr *)ptr, val);
+}
+
+static inline u64 btrfs_node_ptr_generation(struct extent_buffer *eb, int nr)
+{
+ unsigned long ptr;
+ ptr = offsetof(struct btrfs_node, ptrs) +
+ sizeof(struct btrfs_key_ptr) * nr;
+ return btrfs_key_generation(eb, (struct btrfs_key_ptr *)ptr);
+}
+
+static inline void btrfs_set_node_ptr_generation(struct extent_buffer *eb,
+ int nr, u64 val)
+{
+ unsigned long ptr;
+ ptr = offsetof(struct btrfs_node, ptrs) +
+ sizeof(struct btrfs_key_ptr) * nr;
+ btrfs_set_key_generation(eb, (struct btrfs_key_ptr *)ptr, val);
+}
+
+static inline unsigned long btrfs_node_key_ptr_offset(int nr)
+{
+ return offsetof(struct btrfs_node, ptrs) +
+ sizeof(struct btrfs_key_ptr) * nr;
+}
+
+static inline void btrfs_node_key(struct extent_buffer *eb,
+ struct btrfs_disk_key *disk_key, int nr)
+{
+ unsigned long ptr;
+ ptr = btrfs_node_key_ptr_offset(nr);
+ read_eb_member(eb, (struct btrfs_key_ptr *)ptr,
+ struct btrfs_key_ptr, key, disk_key);
+}
+
+static inline void btrfs_set_node_key(struct extent_buffer *eb,
+ struct btrfs_disk_key *disk_key, int nr)
+{
+ unsigned long ptr;
+ ptr = btrfs_node_key_ptr_offset(nr);
+ write_eb_member(eb, (struct btrfs_key_ptr *)ptr,
+ struct btrfs_key_ptr, key, disk_key);
+}
+
+/* struct btrfs_item */
+BTRFS_SETGET_FUNCS(item_offset, struct btrfs_item, offset, 32);
+BTRFS_SETGET_FUNCS(item_size, struct btrfs_item, size, 32);
+
+static inline unsigned long btrfs_item_nr_offset(int nr)
+{
+ return offsetof(struct btrfs_leaf, items) +
+ sizeof(struct btrfs_item) * nr;
+}
+
+static inline struct btrfs_item *btrfs_item_nr(int nr)
+{
+ return (struct btrfs_item *)btrfs_item_nr_offset(nr);
+}
+
+static inline u32 btrfs_item_end(struct extent_buffer *eb,
+ struct btrfs_item *item)
+{
+ return btrfs_item_offset(eb, item) + btrfs_item_size(eb, item);
+}
+
+static inline u32 btrfs_item_end_nr(struct extent_buffer *eb, int nr)
+{
+ return btrfs_item_end(eb, btrfs_item_nr(nr));
+}
+
+static inline u32 btrfs_item_offset_nr(const struct extent_buffer *eb, int nr)
+{
+ return btrfs_item_offset(eb, btrfs_item_nr(nr));
+}
+
+static inline u32 btrfs_item_size_nr(struct extent_buffer *eb, int nr)
+{
+ return btrfs_item_size(eb, btrfs_item_nr(nr));
+}
+
+static inline void btrfs_item_key(struct extent_buffer *eb,
+ struct btrfs_disk_key *disk_key, int nr)
+{
+ struct btrfs_item *item = btrfs_item_nr(nr);
+ read_eb_member(eb, item, struct btrfs_item, key, disk_key);
+}
+
+static inline void btrfs_set_item_key(struct extent_buffer *eb,
+ struct btrfs_disk_key *disk_key, int nr)
+{
+ struct btrfs_item *item = btrfs_item_nr(nr);
+ write_eb_member(eb, item, struct btrfs_item, key, disk_key);
+}
+
+BTRFS_SETGET_FUNCS(dir_log_end, struct btrfs_dir_log_item, end, 64);
+
+/*
+ * struct btrfs_root_ref
+ */
+BTRFS_SETGET_FUNCS(root_ref_dirid, struct btrfs_root_ref, dirid, 64);
+BTRFS_SETGET_FUNCS(root_ref_sequence, struct btrfs_root_ref, sequence, 64);
+BTRFS_SETGET_FUNCS(root_ref_name_len, struct btrfs_root_ref, name_len, 16);
+
+BTRFS_SETGET_STACK_FUNCS(stack_root_ref_dirid, struct btrfs_root_ref, dirid, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_root_ref_sequence, struct btrfs_root_ref, sequence, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_root_ref_name_len, struct btrfs_root_ref, name_len, 16);
+
+/* struct btrfs_dir_item */
+BTRFS_SETGET_FUNCS(dir_data_len, struct btrfs_dir_item, data_len, 16);
+BTRFS_SETGET_FUNCS(dir_type, struct btrfs_dir_item, type, 8);
+BTRFS_SETGET_FUNCS(dir_name_len, struct btrfs_dir_item, name_len, 16);
+BTRFS_SETGET_FUNCS(dir_transid, struct btrfs_dir_item, transid, 64);
+
+BTRFS_SETGET_STACK_FUNCS(stack_dir_data_len, struct btrfs_dir_item, data_len, 16);
+BTRFS_SETGET_STACK_FUNCS(stack_dir_type, struct btrfs_dir_item, type, 8);
+BTRFS_SETGET_STACK_FUNCS(stack_dir_name_len, struct btrfs_dir_item, name_len, 16);
+BTRFS_SETGET_STACK_FUNCS(stack_dir_transid, struct btrfs_dir_item, transid, 64);
+
+static inline void btrfs_dir_item_key(struct extent_buffer *eb,
+ struct btrfs_dir_item *item,
+ struct btrfs_disk_key *key)
+{
+ read_eb_member(eb, item, struct btrfs_dir_item, location, key);
+}
+
+static inline void btrfs_set_dir_item_key(struct extent_buffer *eb,
+ struct btrfs_dir_item *item,
+ struct btrfs_disk_key *key)
+{
+ write_eb_member(eb, item, struct btrfs_dir_item, location, key);
+}
+
+/* struct btrfs_free_space_header */
+BTRFS_SETGET_FUNCS(free_space_entries, struct btrfs_free_space_header,
+ num_entries, 64);
+BTRFS_SETGET_FUNCS(free_space_bitmaps, struct btrfs_free_space_header,
+ num_bitmaps, 64);
+BTRFS_SETGET_FUNCS(free_space_generation, struct btrfs_free_space_header,
+ generation, 64);
+
+static inline void btrfs_free_space_key(struct extent_buffer *eb,
+ struct btrfs_free_space_header *h,
+ struct btrfs_disk_key *key)
+{
+ read_eb_member(eb, h, struct btrfs_free_space_header, location, key);
+}
+
+static inline void btrfs_set_free_space_key(struct extent_buffer *eb,
+ struct btrfs_free_space_header *h,
+ struct btrfs_disk_key *key)
+{
+ write_eb_member(eb, h, struct btrfs_free_space_header, location, key);
+}
+
+/* struct btrfs_disk_key */
+BTRFS_SETGET_STACK_FUNCS(disk_key_objectid, struct btrfs_disk_key,
+ objectid, 64);
+BTRFS_SETGET_STACK_FUNCS(disk_key_offset, struct btrfs_disk_key, offset, 64);
+BTRFS_SETGET_STACK_FUNCS(disk_key_type, struct btrfs_disk_key, type, 8);
+
+static inline void btrfs_disk_key_to_cpu(struct btrfs_key *cpu,
+ struct btrfs_disk_key *disk)
+{
+ cpu->offset = le64_to_cpu(disk->offset);
+ cpu->type = disk->type;
+ cpu->objectid = le64_to_cpu(disk->objectid);
+}
+
+static inline void btrfs_cpu_key_to_disk(struct btrfs_disk_key *disk,
+ const struct btrfs_key *cpu)
+{
+ disk->offset = cpu_to_le64(cpu->offset);
+ disk->type = cpu->type;
+ disk->objectid = cpu_to_le64(cpu->objectid);
+}
+
+static inline void btrfs_node_key_to_cpu(struct extent_buffer *eb,
+ struct btrfs_key *key, int nr)
+{
+ struct btrfs_disk_key disk_key;
+ btrfs_node_key(eb, &disk_key, nr);
+ btrfs_disk_key_to_cpu(key, &disk_key);
+}
+
+static inline void btrfs_item_key_to_cpu(struct extent_buffer *eb,
+ struct btrfs_key *key, int nr)
+{
+ struct btrfs_disk_key disk_key;
+ btrfs_item_key(eb, &disk_key, nr);
+ btrfs_disk_key_to_cpu(key, &disk_key);
+}
+
+static inline void btrfs_dir_item_key_to_cpu(struct extent_buffer *eb,
+ struct btrfs_dir_item *item,
+ struct btrfs_key *key)
+{
+ struct btrfs_disk_key disk_key;
+ btrfs_dir_item_key(eb, item, &disk_key);
+ btrfs_disk_key_to_cpu(key, &disk_key);
+}
+
+/* struct btrfs_header */
+BTRFS_SETGET_HEADER_FUNCS(header_bytenr, struct btrfs_header, bytenr, 64);
+BTRFS_SETGET_HEADER_FUNCS(header_generation, struct btrfs_header,
+ generation, 64);
+BTRFS_SETGET_HEADER_FUNCS(header_owner, struct btrfs_header, owner, 64);
+BTRFS_SETGET_HEADER_FUNCS(header_nritems, struct btrfs_header, nritems, 32);
+BTRFS_SETGET_HEADER_FUNCS(header_flags, struct btrfs_header, flags, 64);
+BTRFS_SETGET_HEADER_FUNCS(header_level, struct btrfs_header, level, 8);
+BTRFS_SETGET_STACK_FUNCS(stack_header_bytenr, struct btrfs_header, bytenr, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_header_nritems, struct btrfs_header, nritems,
+ 32);
+BTRFS_SETGET_STACK_FUNCS(stack_header_owner, struct btrfs_header, owner, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_header_generation, struct btrfs_header,
+ generation, 64);
+
+static inline int btrfs_header_flag(struct extent_buffer *eb, u64 flag)
+{
+ return (btrfs_header_flags(eb) & flag) == flag;
+}
+
+static inline int btrfs_set_header_flag(struct extent_buffer *eb, u64 flag)
+{
+ u64 flags = btrfs_header_flags(eb);
+ btrfs_set_header_flags(eb, flags | flag);
+ return (flags & flag) == flag;
+}
+
+static inline int btrfs_clear_header_flag(struct extent_buffer *eb, u64 flag)
+{
+ u64 flags = btrfs_header_flags(eb);
+ btrfs_set_header_flags(eb, flags & ~flag);
+ return (flags & flag) == flag;
+}
+
+static inline int btrfs_header_backref_rev(struct extent_buffer *eb)
+{
+ u64 flags = btrfs_header_flags(eb);
+ return flags >> BTRFS_BACKREF_REV_SHIFT;
+}
+
+static inline void btrfs_set_header_backref_rev(struct extent_buffer *eb,
+ int rev)
+{
+ u64 flags = btrfs_header_flags(eb);
+ flags &= ~BTRFS_BACKREF_REV_MASK;
+ flags |= (u64)rev << BTRFS_BACKREF_REV_SHIFT;
+ btrfs_set_header_flags(eb, flags);
+}
+
+static inline unsigned long btrfs_header_fsid(void)
+{
+ return offsetof(struct btrfs_header, fsid);
+}
+
+static inline unsigned long btrfs_header_chunk_tree_uuid(struct extent_buffer *eb)
+{
+ return offsetof(struct btrfs_header, chunk_tree_uuid);
+}
+
+static inline u8 *btrfs_header_csum(struct extent_buffer *eb)
+{
+ unsigned long ptr = offsetof(struct btrfs_header, csum);
+ return (u8 *)ptr;
+}
+
+static inline int btrfs_is_leaf(struct extent_buffer *eb)
+{
+ return (btrfs_header_level(eb) == 0);
+}
+
+/* struct btrfs_root_item */
+BTRFS_SETGET_FUNCS(disk_root_generation, struct btrfs_root_item,
+ generation, 64);
+BTRFS_SETGET_FUNCS(disk_root_refs, struct btrfs_root_item, refs, 32);
+BTRFS_SETGET_FUNCS(disk_root_bytenr, struct btrfs_root_item, bytenr, 64);
+BTRFS_SETGET_FUNCS(disk_root_level, struct btrfs_root_item, level, 8);
+
+BTRFS_SETGET_STACK_FUNCS(root_generation, struct btrfs_root_item,
+ generation, 64);
+BTRFS_SETGET_STACK_FUNCS(root_bytenr, struct btrfs_root_item, bytenr, 64);
+BTRFS_SETGET_STACK_FUNCS(root_level, struct btrfs_root_item, level, 8);
+BTRFS_SETGET_STACK_FUNCS(root_dirid, struct btrfs_root_item, root_dirid, 64);
+BTRFS_SETGET_STACK_FUNCS(root_refs, struct btrfs_root_item, refs, 32);
+BTRFS_SETGET_STACK_FUNCS(root_flags, struct btrfs_root_item, flags, 64);
+BTRFS_SETGET_STACK_FUNCS(root_used, struct btrfs_root_item, bytes_used, 64);
+BTRFS_SETGET_STACK_FUNCS(root_limit, struct btrfs_root_item, byte_limit, 64);
+BTRFS_SETGET_STACK_FUNCS(root_last_snapshot, struct btrfs_root_item,
+ last_snapshot, 64);
+BTRFS_SETGET_STACK_FUNCS(root_generation_v2, struct btrfs_root_item,
+ generation_v2, 64);
+BTRFS_SETGET_STACK_FUNCS(root_ctransid, struct btrfs_root_item,
+ ctransid, 64);
+BTRFS_SETGET_STACK_FUNCS(root_otransid, struct btrfs_root_item,
+ otransid, 64);
+BTRFS_SETGET_STACK_FUNCS(root_stransid, struct btrfs_root_item,
+ stransid, 64);
+BTRFS_SETGET_STACK_FUNCS(root_rtransid, struct btrfs_root_item,
+ rtransid, 64);
+
+static inline struct btrfs_timespec* btrfs_root_ctime(
+ struct btrfs_root_item *root_item)
+{
+ unsigned long ptr = (unsigned long)root_item;
+ ptr += offsetof(struct btrfs_root_item, ctime);
+ return (struct btrfs_timespec *)ptr;
+}
+
+static inline struct btrfs_timespec* btrfs_root_otime(
+ struct btrfs_root_item *root_item)
+{
+ unsigned long ptr = (unsigned long)root_item;
+ ptr += offsetof(struct btrfs_root_item, otime);
+ return (struct btrfs_timespec *)ptr;
+}
+
+static inline struct btrfs_timespec* btrfs_root_stime(
+ struct btrfs_root_item *root_item)
+{
+ unsigned long ptr = (unsigned long)root_item;
+ ptr += offsetof(struct btrfs_root_item, stime);
+ return (struct btrfs_timespec *)ptr;
+}
+
+static inline struct btrfs_timespec* btrfs_root_rtime(
+ struct btrfs_root_item *root_item)
+{
+ unsigned long ptr = (unsigned long)root_item;
+ ptr += offsetof(struct btrfs_root_item, rtime);
+ return (struct btrfs_timespec *)ptr;
+}
+
+/* struct btrfs_root_backup */
+BTRFS_SETGET_STACK_FUNCS(backup_tree_root, struct btrfs_root_backup,
+ tree_root, 64);
+BTRFS_SETGET_STACK_FUNCS(backup_tree_root_gen, struct btrfs_root_backup,
+ tree_root_gen, 64);
+BTRFS_SETGET_STACK_FUNCS(backup_tree_root_level, struct btrfs_root_backup,
+ tree_root_level, 8);
+
+BTRFS_SETGET_STACK_FUNCS(backup_chunk_root, struct btrfs_root_backup,
+ chunk_root, 64);
+BTRFS_SETGET_STACK_FUNCS(backup_chunk_root_gen, struct btrfs_root_backup,
+ chunk_root_gen, 64);
+BTRFS_SETGET_STACK_FUNCS(backup_chunk_root_level, struct btrfs_root_backup,
+ chunk_root_level, 8);
+
+BTRFS_SETGET_STACK_FUNCS(backup_extent_root, struct btrfs_root_backup,
+ extent_root, 64);
+BTRFS_SETGET_STACK_FUNCS(backup_extent_root_gen, struct btrfs_root_backup,
+ extent_root_gen, 64);
+BTRFS_SETGET_STACK_FUNCS(backup_extent_root_level, struct btrfs_root_backup,
+ extent_root_level, 8);
+
+BTRFS_SETGET_STACK_FUNCS(backup_fs_root, struct btrfs_root_backup,
+ fs_root, 64);
+BTRFS_SETGET_STACK_FUNCS(backup_fs_root_gen, struct btrfs_root_backup,
+ fs_root_gen, 64);
+BTRFS_SETGET_STACK_FUNCS(backup_fs_root_level, struct btrfs_root_backup,
+ fs_root_level, 8);
+
+BTRFS_SETGET_STACK_FUNCS(backup_dev_root, struct btrfs_root_backup,
+ dev_root, 64);
+BTRFS_SETGET_STACK_FUNCS(backup_dev_root_gen, struct btrfs_root_backup,
+ dev_root_gen, 64);
+BTRFS_SETGET_STACK_FUNCS(backup_dev_root_level, struct btrfs_root_backup,
+ dev_root_level, 8);
+
+BTRFS_SETGET_STACK_FUNCS(backup_csum_root, struct btrfs_root_backup,
+ csum_root, 64);
+BTRFS_SETGET_STACK_FUNCS(backup_csum_root_gen, struct btrfs_root_backup,
+ csum_root_gen, 64);
+BTRFS_SETGET_STACK_FUNCS(backup_csum_root_level, struct btrfs_root_backup,
+ csum_root_level, 8);
+BTRFS_SETGET_STACK_FUNCS(backup_total_bytes, struct btrfs_root_backup,
+ total_bytes, 64);
+BTRFS_SETGET_STACK_FUNCS(backup_bytes_used, struct btrfs_root_backup,
+ bytes_used, 64);
+BTRFS_SETGET_STACK_FUNCS(backup_num_devices, struct btrfs_root_backup,
+ num_devices, 64);
+
+/* struct btrfs_super_block */
+
+BTRFS_SETGET_STACK_FUNCS(super_bytenr, struct btrfs_super_block, bytenr, 64);
+BTRFS_SETGET_STACK_FUNCS(super_flags, struct btrfs_super_block, flags, 64);
+BTRFS_SETGET_STACK_FUNCS(super_generation, struct btrfs_super_block,
+ generation, 64);
+BTRFS_SETGET_STACK_FUNCS(super_root, struct btrfs_super_block, root, 64);
+BTRFS_SETGET_STACK_FUNCS(super_sys_array_size,
+ struct btrfs_super_block, sys_chunk_array_size, 32);
+BTRFS_SETGET_STACK_FUNCS(super_chunk_root_generation,
+ struct btrfs_super_block, chunk_root_generation, 64);
+BTRFS_SETGET_STACK_FUNCS(super_root_level, struct btrfs_super_block,
+ root_level, 8);
+BTRFS_SETGET_STACK_FUNCS(super_chunk_root, struct btrfs_super_block,
+ chunk_root, 64);
+BTRFS_SETGET_STACK_FUNCS(super_chunk_root_level, struct btrfs_super_block,
+ chunk_root_level, 8);
+BTRFS_SETGET_STACK_FUNCS(super_log_root, struct btrfs_super_block,
+ log_root, 64);
+BTRFS_SETGET_STACK_FUNCS(super_log_root_transid, struct btrfs_super_block,
+ log_root_transid, 64);
+BTRFS_SETGET_STACK_FUNCS(super_log_root_level, struct btrfs_super_block,
+ log_root_level, 8);
+BTRFS_SETGET_STACK_FUNCS(super_total_bytes, struct btrfs_super_block,
+ total_bytes, 64);
+BTRFS_SETGET_STACK_FUNCS(super_bytes_used, struct btrfs_super_block,
+ bytes_used, 64);
+BTRFS_SETGET_STACK_FUNCS(super_sectorsize, struct btrfs_super_block,
+ sectorsize, 32);
+BTRFS_SETGET_STACK_FUNCS(super_nodesize, struct btrfs_super_block,
+ nodesize, 32);
+BTRFS_SETGET_STACK_FUNCS(super_stripesize, struct btrfs_super_block,
+ stripesize, 32);
+BTRFS_SETGET_STACK_FUNCS(super_root_dir, struct btrfs_super_block,
+ root_dir_objectid, 64);
+BTRFS_SETGET_STACK_FUNCS(super_num_devices, struct btrfs_super_block,
+ num_devices, 64);
+BTRFS_SETGET_STACK_FUNCS(super_compat_flags, struct btrfs_super_block,
+ compat_flags, 64);
+BTRFS_SETGET_STACK_FUNCS(super_compat_ro_flags, struct btrfs_super_block,
+ compat_ro_flags, 64);
+BTRFS_SETGET_STACK_FUNCS(super_incompat_flags, struct btrfs_super_block,
+ incompat_flags, 64);
+BTRFS_SETGET_STACK_FUNCS(super_csum_type, struct btrfs_super_block,
+ csum_type, 16);
+BTRFS_SETGET_STACK_FUNCS(super_cache_generation, struct btrfs_super_block,
+ cache_generation, 64);
+BTRFS_SETGET_STACK_FUNCS(super_uuid_tree_generation, struct btrfs_super_block,
+ uuid_tree_generation, 64);
+BTRFS_SETGET_STACK_FUNCS(super_magic, struct btrfs_super_block, magic, 64);
+
+static inline unsigned long btrfs_leaf_data(struct extent_buffer *l)
+{
+ return offsetof(struct btrfs_leaf, items);
+}
+
+/* struct btrfs_file_extent_item */
+BTRFS_SETGET_FUNCS(file_extent_type, struct btrfs_file_extent_item, type, 8);
+BTRFS_SETGET_STACK_FUNCS(stack_file_extent_type, struct btrfs_file_extent_item, type, 8);
+
+static inline unsigned long btrfs_file_extent_inline_start(struct
+ btrfs_file_extent_item *e)
+{
+ unsigned long offset = (unsigned long)e;
+ offset += offsetof(struct btrfs_file_extent_item, disk_bytenr);
+ return offset;
+}
+
+static inline u32 btrfs_file_extent_calc_inline_size(u32 datasize)
+{
+ return offsetof(struct btrfs_file_extent_item, disk_bytenr) + datasize;
+}
+
+BTRFS_SETGET_FUNCS(file_extent_disk_bytenr, struct btrfs_file_extent_item,
+ disk_bytenr, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_file_extent_disk_bytenr, struct btrfs_file_extent_item,
+ disk_bytenr, 64);
+BTRFS_SETGET_FUNCS(file_extent_generation, struct btrfs_file_extent_item,
+ generation, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_file_extent_generation, struct btrfs_file_extent_item,
+ generation, 64);
+BTRFS_SETGET_FUNCS(file_extent_disk_num_bytes, struct btrfs_file_extent_item,
+ disk_num_bytes, 64);
+BTRFS_SETGET_FUNCS(file_extent_offset, struct btrfs_file_extent_item,
+ offset, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_file_extent_offset, struct btrfs_file_extent_item,
+ offset, 64);
+BTRFS_SETGET_FUNCS(file_extent_num_bytes, struct btrfs_file_extent_item,
+ num_bytes, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_file_extent_num_bytes, struct btrfs_file_extent_item,
+ num_bytes, 64);
+BTRFS_SETGET_FUNCS(file_extent_ram_bytes, struct btrfs_file_extent_item,
+ ram_bytes, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_file_extent_ram_bytes, struct btrfs_file_extent_item,
+ ram_bytes, 64);
+BTRFS_SETGET_FUNCS(file_extent_compression, struct btrfs_file_extent_item,
+ compression, 8);
+BTRFS_SETGET_STACK_FUNCS(stack_file_extent_compression, struct btrfs_file_extent_item,
+ compression, 8);
+BTRFS_SETGET_FUNCS(file_extent_encryption, struct btrfs_file_extent_item,
+ encryption, 8);
+BTRFS_SETGET_FUNCS(file_extent_other_encoding, struct btrfs_file_extent_item,
+ other_encoding, 16);
+
+/* btrfs_qgroup_status_item */
+BTRFS_SETGET_FUNCS(qgroup_status_version, struct btrfs_qgroup_status_item,
+ version, 64);
+BTRFS_SETGET_FUNCS(qgroup_status_generation, struct btrfs_qgroup_status_item,
+ generation, 64);
+BTRFS_SETGET_FUNCS(qgroup_status_flags, struct btrfs_qgroup_status_item,
+ flags, 64);
+BTRFS_SETGET_FUNCS(qgroup_status_rescan, struct btrfs_qgroup_status_item,
+ rescan, 64);
+
+BTRFS_SETGET_STACK_FUNCS(stack_qgroup_status_version,
+ struct btrfs_qgroup_status_item, version, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_qgroup_status_generation,
+ struct btrfs_qgroup_status_item, generation, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_qgroup_status_flags,
+ struct btrfs_qgroup_status_item, flags, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_qgroup_status_rescan,
+ struct btrfs_qgroup_status_item, rescan, 64);
+
+/* btrfs_qgroup_info_item */
+BTRFS_SETGET_FUNCS(qgroup_info_generation, struct btrfs_qgroup_info_item,
+ generation, 64);
+BTRFS_SETGET_FUNCS(qgroup_info_referenced, struct btrfs_qgroup_info_item,
+ rfer, 64);
+BTRFS_SETGET_FUNCS(qgroup_info_referenced_compressed,
+ struct btrfs_qgroup_info_item, rfer_cmpr, 64);
+BTRFS_SETGET_FUNCS(qgroup_info_exclusive, struct btrfs_qgroup_info_item,
+ excl, 64);
+BTRFS_SETGET_FUNCS(qgroup_info_exclusive_compressed,
+ struct btrfs_qgroup_info_item, excl_cmpr, 64);
+
+BTRFS_SETGET_STACK_FUNCS(stack_qgroup_info_generation,
+ struct btrfs_qgroup_info_item, generation, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_qgroup_info_referenced,
+ struct btrfs_qgroup_info_item, rfer, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_qgroup_info_referenced_compressed,
+ struct btrfs_qgroup_info_item, rfer_cmpr, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_qgroup_info_exclusive,
+ struct btrfs_qgroup_info_item, excl, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_qgroup_info_exclusive_compressed,
+ struct btrfs_qgroup_info_item, excl_cmpr, 64);
+
+/* btrfs_qgroup_limit_item */
+BTRFS_SETGET_FUNCS(qgroup_limit_flags, struct btrfs_qgroup_limit_item,
+ flags, 64);
+BTRFS_SETGET_FUNCS(qgroup_limit_max_referenced, struct btrfs_qgroup_limit_item,
+ max_rfer, 64);
+BTRFS_SETGET_FUNCS(qgroup_limit_max_exclusive, struct btrfs_qgroup_limit_item,
+ max_excl, 64);
+BTRFS_SETGET_FUNCS(qgroup_limit_rsv_referenced, struct btrfs_qgroup_limit_item,
+ rsv_rfer, 64);
+BTRFS_SETGET_FUNCS(qgroup_limit_rsv_exclusive, struct btrfs_qgroup_limit_item,
+ rsv_excl, 64);
+
+BTRFS_SETGET_STACK_FUNCS(stack_qgroup_limit_flags,
+ struct btrfs_qgroup_limit_item, flags, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_qgroup_limit_max_referenced,
+ struct btrfs_qgroup_limit_item, max_rfer, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_qgroup_limit_max_exclusive,
+ struct btrfs_qgroup_limit_item, max_excl, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_qgroup_limit_rsv_referenced,
+ struct btrfs_qgroup_limit_item, rsv_rfer, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_qgroup_limit_rsv_exclusive,
+ struct btrfs_qgroup_limit_item, rsv_excl, 64);
+
+/* btrfs_balance_item */
+BTRFS_SETGET_FUNCS(balance_item_flags, struct btrfs_balance_item, flags, 64);
+
+static inline struct btrfs_disk_balance_args* btrfs_balance_item_data(
+ struct extent_buffer *eb, struct btrfs_balance_item *bi)
+{
+ unsigned long offset = (unsigned long)bi;
+ struct btrfs_balance_item *p;
+ p = (struct btrfs_balance_item *)(eb->data + offset);
+ return &p->data;
+}
+
+static inline struct btrfs_disk_balance_args* btrfs_balance_item_meta(
+ struct extent_buffer *eb, struct btrfs_balance_item *bi)
+{
+ unsigned long offset = (unsigned long)bi;
+ struct btrfs_balance_item *p;
+ p = (struct btrfs_balance_item *)(eb->data + offset);
+ return &p->meta;
+}
+
+static inline struct btrfs_disk_balance_args* btrfs_balance_item_sys(
+ struct extent_buffer *eb, struct btrfs_balance_item *bi)
+{
+ unsigned long offset = (unsigned long)bi;
+ struct btrfs_balance_item *p;
+ p = (struct btrfs_balance_item *)(eb->data + offset);
+ return &p->sys;
+}
+
+static inline u64 btrfs_dev_stats_value(const struct extent_buffer *eb,
+ const struct btrfs_dev_stats_item *ptr,
+ int index)
+{
+ u64 val;
+
+ read_extent_buffer(eb, &val,
+ offsetof(struct btrfs_dev_stats_item, values) +
+ ((unsigned long)ptr) + (index * sizeof(u64)),
+ sizeof(val));
+ return val;
+}
+
+/*
+ * this returns the number of bytes used by the item on disk, minus the
+ * size of any extent headers. If a file is compressed on disk, this is
+ * the compressed size
+ */
+static inline u32 btrfs_file_extent_inline_item_len(struct extent_buffer *eb,
+ struct btrfs_item *e)
+{
+ unsigned long offset;
+ offset = offsetof(struct btrfs_file_extent_item, disk_bytenr);
+ return btrfs_item_size(eb, e) - offset;
+}
+
+#define btrfs_fs_incompat(fs_info, opt) \
+ __btrfs_fs_incompat((fs_info), BTRFS_FEATURE_INCOMPAT_##opt)
+
+static inline bool __btrfs_fs_incompat(struct btrfs_fs_info *fs_info, u64 flag)
+{
+ struct btrfs_super_block *disk_super;
+ disk_super = fs_info->super_copy;
+ return !!(btrfs_super_incompat_flags(disk_super) & flag);
+}
+
+#define btrfs_fs_compat_ro(fs_info, opt) \
+ __btrfs_fs_compat_ro((fs_info), BTRFS_FEATURE_COMPAT_RO_##opt)
+
+static inline int __btrfs_fs_compat_ro(struct btrfs_fs_info *fs_info, u64 flag)
+{
+ struct btrfs_super_block *disk_super;
+ disk_super = fs_info->super_copy;
+ return !!(btrfs_super_compat_ro_flags(disk_super) & flag);
+}
+
+/* helper function to cast into the data area of the leaf. */
+#define btrfs_item_ptr(leaf, slot, type) \
+ ((type *)(btrfs_leaf_data(leaf) + \
+ btrfs_item_offset_nr(leaf, slot)))
+
+#define btrfs_item_ptr_offset(leaf, slot) \
+ ((unsigned long)(btrfs_leaf_data(leaf) + \
+ btrfs_item_offset_nr(leaf, slot)))
+
+static inline u64 btrfs_name_hash(const char *name, int len)
+{
+ return (u64)crc32c((u32)~1, (u8 *)name, len);
+}
+
+/*
+ * Figure the key offset of an extended inode ref
+ */
+static inline u64 btrfs_extref_hash(u64 parent_objectid, const char *name,
+ int len)
+{
+ return crc32(parent_objectid, (u8 *)name, len);
+}
+
+union btrfs_tree_node {
+ struct btrfs_header header;
+ struct btrfs_leaf leaf;
+ struct btrfs_node node;
+};
+
+#define btrfs_path_item_ptr(p,t) \
+ ((t *) btrfs_path_leaf_data((p)))
+
+u16 btrfs_super_csum_size(const struct btrfs_super_block *s);
+const char *btrfs_super_csum_name(u16 csum_type);
+u16 btrfs_csum_type_size(u16 csum_type);
+size_t btrfs_super_num_csums(void);
+
+/* root-tree.c */
+int btrfs_find_last_root(struct btrfs_root *root, u64 objectid,
+ struct btrfs_root_item *item, struct btrfs_key *key);
+
+/* dir-item.c */
+struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_path *path, u64 dir,
+ const char *name, int name_len,
+ int mod);
+typedef int (*btrfs_iter_dir_callback_t)(struct btrfs_root *root,
+ struct extent_buffer *eb,
+ struct btrfs_dir_item *di);
+int btrfs_iter_dir(struct btrfs_root *root, u64 ino,
+ btrfs_iter_dir_callback_t callback);
+/* inode.c */
+int btrfs_lookup_path(struct btrfs_root *root, u64 ino, const char *filename,
+ struct btrfs_root **root_ret, u64 *ino_ret,
+ u8 *type_ret, int symlink_limit);
+int btrfs_read_extent_inline(struct btrfs_path *path,
+ struct btrfs_file_extent_item *fi, char *dest);
+int btrfs_read_extent_reg(struct btrfs_path *path,
+ struct btrfs_file_extent_item *fi, u64 offset,
+ int len, char *dest);
+
+/* ctree.c */
+int btrfs_comp_cpu_keys(const struct btrfs_key *k1, const struct btrfs_key *k2);
+enum btrfs_tree_block_status
+btrfs_check_node(struct btrfs_fs_info *fs_info,
+ struct btrfs_disk_key *parent_key, struct extent_buffer *buf);
+enum btrfs_tree_block_status
+btrfs_check_leaf(struct btrfs_fs_info *fs_info,
+ struct btrfs_disk_key *parent_key, struct extent_buffer *buf);
+struct extent_buffer *read_node_slot(struct btrfs_fs_info *fs_info,
+ struct extent_buffer *parent, int slot);
+int btrfs_previous_item(struct btrfs_root *root,
+ struct btrfs_path *path, u64 min_objectid,
+ int type);
+int btrfs_next_sibling_tree_block(struct btrfs_fs_info *fs_info,
+ struct btrfs_path *path);
+/*
+ * Walk up the tree as far as necessary to find the next leaf.
+ *
+ * returns 0 if it found something or 1 if there are no greater leaves.
+ * returns < 0 on io errors.
+ */
+static inline int btrfs_next_leaf(struct btrfs_root *root,
+ struct btrfs_path *path)
+{
+ path->lowest_level = 0;
+ return btrfs_next_sibling_tree_block(root->fs_info, path);
+}
+
+static inline int btrfs_next_item(struct btrfs_root *root,
+ struct btrfs_path *p)
+{
+ ++p->slots[0];
+ if (p->slots[0] >= btrfs_header_nritems(p->nodes[0]))
+ return btrfs_next_leaf(root, p);
+ return 0;
+}
+
+int btrfs_prev_leaf(struct btrfs_root *root, struct btrfs_path *path);
+int btrfs_leaf_free_space(struct extent_buffer *leaf);
+int btrfs_search_slot(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root, const struct btrfs_key *key,
+ struct btrfs_path *p, int ins_len, int cow);
+int btrfs_search_slot_for_read(struct btrfs_root *root,
+ const struct btrfs_key *key,
+ struct btrfs_path *p, int find_higher,
+ int return_any);
+void btrfs_release_path(struct btrfs_path *p);
+struct btrfs_path *btrfs_alloc_path(void);
+void btrfs_free_path(struct btrfs_path *p);
+static inline void btrfs_init_path(struct btrfs_path *p)
+{
+ memset(p, 0, sizeof(*p));
+}
+int btrfs_bin_search(struct extent_buffer *eb, const struct btrfs_key *key,
+ int *slot);
+int btrfs_find_item(struct btrfs_root *fs_root, struct btrfs_path *found_path,
+ u64 iobjectid, u64 ioff, u8 key_type,
+ struct btrfs_key *found_key);
+#endif /* __BTRFS_CTREE_H__ */
diff --git a/roms/u-boot/fs/btrfs/dev.c b/roms/u-boot/fs/btrfs/dev.c
new file mode 100644
index 000000000..1e5b83235
--- /dev/null
+++ b/roms/u-boot/fs/btrfs/dev.c
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * BTRFS filesystem implementation for U-Boot
+ *
+ * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz
+ */
+
+#include <common.h>
+#include <blk.h>
+#include <compiler.h>
+#include <fs_internal.h>
+
+struct blk_desc *btrfs_blk_desc;
+struct disk_partition *btrfs_part_info;
+
+int btrfs_devread(u64 address, int byte_len, void *buf)
+{
+ lbaint_t sector;
+ int byte_offset;
+
+ sector = address >> btrfs_blk_desc->log2blksz;
+ byte_offset = address % btrfs_blk_desc->blksz;
+
+ return fs_devread(btrfs_blk_desc, btrfs_part_info, sector, byte_offset,
+ byte_len, buf);
+}
diff --git a/roms/u-boot/fs/btrfs/dir-item.c b/roms/u-boot/fs/btrfs/dir-item.c
new file mode 100644
index 000000000..aab197a6d
--- /dev/null
+++ b/roms/u-boot/fs/btrfs/dir-item.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * BTRFS filesystem implementation for U-Boot
+ *
+ * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz
+ */
+
+#include "btrfs.h"
+#include "disk-io.h"
+
+static int verify_dir_item(struct btrfs_root *root,
+ struct extent_buffer *leaf,
+ struct btrfs_dir_item *dir_item)
+{
+ u16 namelen = BTRFS_NAME_LEN;
+ u8 type = btrfs_dir_type(leaf, dir_item);
+
+ if (type == BTRFS_FT_XATTR)
+ namelen = XATTR_NAME_MAX;
+
+ if (btrfs_dir_name_len(leaf, dir_item) > namelen) {
+ fprintf(stderr, "invalid dir item name len: %u\n",
+ (unsigned)btrfs_dir_data_len(leaf, dir_item));
+ return 1;
+ }
+
+ /* BTRFS_MAX_XATTR_SIZE is the same for all dir items */
+ if ((btrfs_dir_data_len(leaf, dir_item) +
+ btrfs_dir_name_len(leaf, dir_item)) >
+ BTRFS_MAX_XATTR_SIZE(root->fs_info)) {
+ fprintf(stderr, "invalid dir item name + data len: %u + %u\n",
+ (unsigned)btrfs_dir_name_len(leaf, dir_item),
+ (unsigned)btrfs_dir_data_len(leaf, dir_item));
+ return 1;
+ }
+
+ return 0;
+}
+
+struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_root *root,
+ struct btrfs_path *path,
+ const char *name, int name_len)
+{
+ struct btrfs_dir_item *dir_item;
+ unsigned long name_ptr;
+ u32 total_len;
+ u32 cur = 0;
+ u32 this_len;
+ struct extent_buffer *leaf;
+
+ leaf = path->nodes[0];
+ dir_item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_dir_item);
+ total_len = btrfs_item_size_nr(leaf, path->slots[0]);
+ if (verify_dir_item(root, leaf, dir_item))
+ return NULL;
+
+ while(cur < total_len) {
+ this_len = sizeof(*dir_item) +
+ btrfs_dir_name_len(leaf, dir_item) +
+ btrfs_dir_data_len(leaf, dir_item);
+ if (this_len > (total_len - cur)) {
+ fprintf(stderr, "invalid dir item size\n");
+ return NULL;
+ }
+
+ name_ptr = (unsigned long)(dir_item + 1);
+
+ if (btrfs_dir_name_len(leaf, dir_item) == name_len &&
+ memcmp_extent_buffer(leaf, name, name_ptr, name_len) == 0)
+ return dir_item;
+
+ cur += this_len;
+ dir_item = (struct btrfs_dir_item *)((char *)dir_item +
+ this_len);
+ }
+ return NULL;
+}
+
+struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_path *path, u64 dir,
+ const char *name, int name_len,
+ int mod)
+{
+ int ret;
+ struct btrfs_key key;
+ int ins_len = mod < 0 ? -1 : 0;
+ int cow = mod != 0;
+ struct btrfs_key found_key;
+ struct extent_buffer *leaf;
+
+ key.objectid = dir;
+ key.type = BTRFS_DIR_ITEM_KEY;
+
+ key.offset = btrfs_name_hash(name, name_len);
+
+ ret = btrfs_search_slot(trans, root, &key, path, ins_len, cow);
+ if (ret < 0)
+ return ERR_PTR(ret);
+ if (ret > 0) {
+ if (path->slots[0] == 0)
+ return NULL;
+ path->slots[0]--;
+ }
+
+ leaf = path->nodes[0];
+ btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
+
+ if (found_key.objectid != dir ||
+ found_key.type != BTRFS_DIR_ITEM_KEY ||
+ found_key.offset != key.offset)
+ return NULL;
+
+ return btrfs_match_dir_item_name(root, path, name, name_len);
+}
+
+int btrfs_iter_dir(struct btrfs_root *root, u64 ino,
+ btrfs_iter_dir_callback_t callback)
+{
+ struct btrfs_path path;
+ struct btrfs_key key;
+ int ret;
+
+ btrfs_init_path(&path);
+ key.objectid = ino;
+ key.type = BTRFS_DIR_INDEX_KEY;
+ key.offset = 0;
+
+ ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
+ if (ret < 0)
+ return ret;
+ /* Should not happen */
+ if (ret == 0) {
+ ret = -EUCLEAN;
+ goto out;
+ }
+ if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) {
+ ret = btrfs_next_leaf(root, &path);
+ if (ret < 0)
+ goto out;
+ if (ret > 0) {
+ ret = 0;
+ goto out;
+ }
+ }
+ do {
+ struct btrfs_dir_item *di;
+
+ btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
+ if (key.objectid != ino || key.type != BTRFS_DIR_INDEX_KEY)
+ break;
+ di = btrfs_item_ptr(path.nodes[0], path.slots[0],
+ struct btrfs_dir_item);
+ if (verify_dir_item(root, path.nodes[0], di)) {
+ ret = -EUCLEAN;
+ goto out;
+ }
+ ret = callback(root, path.nodes[0], di);
+ if (ret < 0)
+ goto out;
+ } while (!(ret = btrfs_next_item(root, &path)));
+
+ if (ret > 0)
+ ret = 0;
+out:
+ btrfs_release_path(&path);
+ return ret;
+}
diff --git a/roms/u-boot/fs/btrfs/disk-io.c b/roms/u-boot/fs/btrfs/disk-io.c
new file mode 100644
index 000000000..349411c3c
--- /dev/null
+++ b/roms/u-boot/fs/btrfs/disk-io.c
@@ -0,0 +1,1058 @@
+// SPDX-License-Identifier: GPL-2.0+
+#include <common.h>
+#include <fs_internal.h>
+#include <uuid.h>
+#include <memalign.h>
+#include "kernel-shared/btrfs_tree.h"
+#include "common/rbtree-utils.h"
+#include "disk-io.h"
+#include "ctree.h"
+#include "btrfs.h"
+#include "volumes.h"
+#include "extent-io.h"
+#include "crypto/hash.h"
+
+/* specified errno for check_tree_block */
+#define BTRFS_BAD_BYTENR (-1)
+#define BTRFS_BAD_FSID (-2)
+#define BTRFS_BAD_LEVEL (-3)
+#define BTRFS_BAD_NRITEMS (-4)
+
+/* Calculate max possible nritems for a leaf/node */
+static u32 max_nritems(u8 level, u32 nodesize)
+{
+
+ if (level == 0)
+ return ((nodesize - sizeof(struct btrfs_header)) /
+ sizeof(struct btrfs_item));
+ return ((nodesize - sizeof(struct btrfs_header)) /
+ sizeof(struct btrfs_key_ptr));
+}
+
+static int check_tree_block(struct btrfs_fs_info *fs_info,
+ struct extent_buffer *buf)
+{
+
+ struct btrfs_fs_devices *fs_devices = fs_info->fs_devices;
+ u32 nodesize = fs_info->nodesize;
+ bool fsid_match = false;
+ int ret = BTRFS_BAD_FSID;
+
+ if (buf->start != btrfs_header_bytenr(buf))
+ return BTRFS_BAD_BYTENR;
+ if (btrfs_header_level(buf) >= BTRFS_MAX_LEVEL)
+ return BTRFS_BAD_LEVEL;
+ if (btrfs_header_nritems(buf) > max_nritems(btrfs_header_level(buf),
+ nodesize))
+ return BTRFS_BAD_NRITEMS;
+
+ /* Only leaf can be empty */
+ if (btrfs_header_nritems(buf) == 0 &&
+ btrfs_header_level(buf) != 0)
+ return BTRFS_BAD_NRITEMS;
+
+ while (fs_devices) {
+ /*
+ * Checking the incompat flag is only valid for the current
+ * fs. For seed devices it's forbidden to have their uuid
+ * changed so reading ->fsid in this case is fine
+ */
+ if (fs_devices == fs_info->fs_devices &&
+ btrfs_fs_incompat(fs_info, METADATA_UUID))
+ fsid_match = !memcmp_extent_buffer(buf,
+ fs_devices->metadata_uuid,
+ btrfs_header_fsid(),
+ BTRFS_FSID_SIZE);
+ else
+ fsid_match = !memcmp_extent_buffer(buf,
+ fs_devices->fsid,
+ btrfs_header_fsid(),
+ BTRFS_FSID_SIZE);
+
+
+ if (fsid_match) {
+ ret = 0;
+ break;
+ }
+ fs_devices = fs_devices->seed;
+ }
+ return ret;
+}
+
+static void print_tree_block_error(struct btrfs_fs_info *fs_info,
+ struct extent_buffer *eb,
+ int err)
+{
+ char fs_uuid[BTRFS_UUID_UNPARSED_SIZE] = {'\0'};
+ char found_uuid[BTRFS_UUID_UNPARSED_SIZE] = {'\0'};
+ u8 buf[BTRFS_UUID_SIZE];
+
+ if (!err)
+ return;
+
+ fprintf(stderr, "bad tree block %llu, ", eb->start);
+ switch (err) {
+ case BTRFS_BAD_FSID:
+ read_extent_buffer(eb, buf, btrfs_header_fsid(),
+ BTRFS_UUID_SIZE);
+ uuid_unparse(buf, found_uuid);
+ uuid_unparse(fs_info->fs_devices->metadata_uuid, fs_uuid);
+ fprintf(stderr, "fsid mismatch, want=%s, have=%s\n",
+ fs_uuid, found_uuid);
+ break;
+ case BTRFS_BAD_BYTENR:
+ fprintf(stderr, "bytenr mismatch, want=%llu, have=%llu\n",
+ eb->start, btrfs_header_bytenr(eb));
+ break;
+ case BTRFS_BAD_LEVEL:
+ fprintf(stderr, "bad level, %u > %d\n",
+ btrfs_header_level(eb), BTRFS_MAX_LEVEL);
+ break;
+ case BTRFS_BAD_NRITEMS:
+ fprintf(stderr, "invalid nr_items: %u\n",
+ btrfs_header_nritems(eb));
+ break;
+ }
+}
+
+int btrfs_csum_data(u16 csum_type, const u8 *data, u8 *out, size_t len)
+{
+ memset(out, 0, BTRFS_CSUM_SIZE);
+
+ switch (csum_type) {
+ case BTRFS_CSUM_TYPE_CRC32:
+ return hash_crc32c(data, len, out);
+ case BTRFS_CSUM_TYPE_XXHASH:
+ return hash_xxhash(data, len, out);
+ case BTRFS_CSUM_TYPE_SHA256:
+ return hash_sha256(data, len, out);
+ default:
+ printf("Unknown csum type %d\n", csum_type);
+ return -EINVAL;
+ }
+}
+
+/*
+ * Check if the super is valid:
+ * - nodesize/sectorsize - minimum, maximum, alignment
+ * - tree block starts - alignment
+ * - number of devices - something sane
+ * - sys array size - maximum
+ */
+static int btrfs_check_super(struct btrfs_super_block *sb)
+{
+ u8 result[BTRFS_CSUM_SIZE];
+ u16 csum_type;
+ int csum_size;
+ u8 *metadata_uuid;
+
+ if (btrfs_super_magic(sb) != BTRFS_MAGIC)
+ return -EIO;
+
+ csum_type = btrfs_super_csum_type(sb);
+ if (csum_type >= btrfs_super_num_csums()) {
+ error("unsupported checksum algorithm %u", csum_type);
+ return -EIO;
+ }
+ csum_size = btrfs_super_csum_size(sb);
+
+ btrfs_csum_data(csum_type, (u8 *)sb + BTRFS_CSUM_SIZE,
+ result, BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE);
+
+ if (memcmp(result, sb->csum, csum_size)) {
+ error("superblock checksum mismatch");
+ return -EIO;
+ }
+ if (btrfs_super_root_level(sb) >= BTRFS_MAX_LEVEL) {
+ error("tree_root level too big: %d >= %d",
+ btrfs_super_root_level(sb), BTRFS_MAX_LEVEL);
+ goto error_out;
+ }
+ if (btrfs_super_chunk_root_level(sb) >= BTRFS_MAX_LEVEL) {
+ error("chunk_root level too big: %d >= %d",
+ btrfs_super_chunk_root_level(sb), BTRFS_MAX_LEVEL);
+ goto error_out;
+ }
+ if (btrfs_super_log_root_level(sb) >= BTRFS_MAX_LEVEL) {
+ error("log_root level too big: %d >= %d",
+ btrfs_super_log_root_level(sb), BTRFS_MAX_LEVEL);
+ goto error_out;
+ }
+
+ if (!IS_ALIGNED(btrfs_super_root(sb), 4096)) {
+ error("tree_root block unaligned: %llu", btrfs_super_root(sb));
+ goto error_out;
+ }
+ if (!IS_ALIGNED(btrfs_super_chunk_root(sb), 4096)) {
+ error("chunk_root block unaligned: %llu",
+ btrfs_super_chunk_root(sb));
+ goto error_out;
+ }
+ if (!IS_ALIGNED(btrfs_super_log_root(sb), 4096)) {
+ error("log_root block unaligned: %llu",
+ btrfs_super_log_root(sb));
+ goto error_out;
+ }
+ if (btrfs_super_nodesize(sb) < 4096) {
+ error("nodesize too small: %u < 4096",
+ btrfs_super_nodesize(sb));
+ goto error_out;
+ }
+ if (!IS_ALIGNED(btrfs_super_nodesize(sb), 4096)) {
+ error("nodesize unaligned: %u", btrfs_super_nodesize(sb));
+ goto error_out;
+ }
+ if (btrfs_super_sectorsize(sb) < 4096) {
+ error("sectorsize too small: %u < 4096",
+ btrfs_super_sectorsize(sb));
+ goto error_out;
+ }
+ if (!IS_ALIGNED(btrfs_super_sectorsize(sb), 4096)) {
+ error("sectorsize unaligned: %u", btrfs_super_sectorsize(sb));
+ goto error_out;
+ }
+ if (btrfs_super_total_bytes(sb) == 0) {
+ error("invalid total_bytes 0");
+ goto error_out;
+ }
+ if (btrfs_super_bytes_used(sb) < 6 * btrfs_super_nodesize(sb)) {
+ error("invalid bytes_used %llu", btrfs_super_bytes_used(sb));
+ goto error_out;
+ }
+ if ((btrfs_super_stripesize(sb) != 4096)
+ && (btrfs_super_stripesize(sb) != btrfs_super_sectorsize(sb))) {
+ error("invalid stripesize %u", btrfs_super_stripesize(sb));
+ goto error_out;
+ }
+
+ if (btrfs_super_incompat_flags(sb) & BTRFS_FEATURE_INCOMPAT_METADATA_UUID)
+ metadata_uuid = sb->metadata_uuid;
+ else
+ metadata_uuid = sb->fsid;
+
+ if (memcmp(metadata_uuid, sb->dev_item.fsid, BTRFS_FSID_SIZE) != 0) {
+ char fsid[BTRFS_UUID_UNPARSED_SIZE];
+ char dev_fsid[BTRFS_UUID_UNPARSED_SIZE];
+
+ uuid_unparse(sb->metadata_uuid, fsid);
+ uuid_unparse(sb->dev_item.fsid, dev_fsid);
+ error("dev_item UUID does not match fsid: %s != %s",
+ dev_fsid, fsid);
+ goto error_out;
+ }
+
+ /*
+ * Hint to catch really bogus numbers, bitflips or so
+ */
+ if (btrfs_super_num_devices(sb) > (1UL << 31)) {
+ error("suspicious number of devices: %llu",
+ btrfs_super_num_devices(sb));
+ }
+
+ if (btrfs_super_num_devices(sb) == 0) {
+ error("number of devices is 0");
+ goto error_out;
+ }
+
+ /*
+ * Obvious sys_chunk_array corruptions, it must hold at least one key
+ * and one chunk
+ */
+ if (btrfs_super_sys_array_size(sb) > BTRFS_SYSTEM_CHUNK_ARRAY_SIZE) {
+ error("system chunk array too big %u > %u",
+ btrfs_super_sys_array_size(sb),
+ BTRFS_SYSTEM_CHUNK_ARRAY_SIZE);
+ goto error_out;
+ }
+ if (btrfs_super_sys_array_size(sb) < sizeof(struct btrfs_disk_key)
+ + sizeof(struct btrfs_chunk)) {
+ error("system chunk array too small %u < %zu",
+ btrfs_super_sys_array_size(sb),
+ sizeof(struct btrfs_disk_key) +
+ sizeof(struct btrfs_chunk));
+ goto error_out;
+ }
+
+ return 0;
+
+error_out:
+ error("superblock checksum matches but it has invalid members");
+ return -EIO;
+}
+
+/*
+ * btrfs_read_dev_super - read a valid primary superblock from a block device
+ * @desc,@part: file descriptor of the device
+ * @sb: buffer where the superblock is going to be read in
+ *
+ * Unlike the btrfs-progs/kernel version, here we ony care about the first
+ * super block, thus it's much simpler.
+ */
+int btrfs_read_dev_super(struct blk_desc *desc, struct disk_partition *part,
+ struct btrfs_super_block *sb)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(char, tmp, BTRFS_SUPER_INFO_SIZE);
+ struct btrfs_super_block *buf = (struct btrfs_super_block *)tmp;
+ int ret;
+
+ ret = __btrfs_devread(desc, part, tmp, BTRFS_SUPER_INFO_SIZE,
+ BTRFS_SUPER_INFO_OFFSET);
+ if (ret < BTRFS_SUPER_INFO_SIZE)
+ return -EIO;
+
+ if (btrfs_super_bytenr(buf) != BTRFS_SUPER_INFO_OFFSET)
+ return -EIO;
+
+ if (btrfs_check_super(buf))
+ return -EIO;
+
+ memcpy(sb, buf, BTRFS_SUPER_INFO_SIZE);
+ return 0;
+}
+
+static int __csum_tree_block_size(struct extent_buffer *buf, u16 csum_size,
+ int verify, int silent, u16 csum_type)
+{
+ u8 result[BTRFS_CSUM_SIZE];
+ u32 len;
+
+ len = buf->len - BTRFS_CSUM_SIZE;
+ btrfs_csum_data(csum_type, (u8 *)buf->data + BTRFS_CSUM_SIZE,
+ result, len);
+
+ if (verify) {
+ if (memcmp_extent_buffer(buf, result, 0, csum_size)) {
+ /* FIXME: format */
+ if (!silent)
+ printk("checksum verify failed on %llu found %08X wanted %08X\n",
+ (unsigned long long)buf->start,
+ result[0],
+ buf->data[0]);
+ return 1;
+ }
+ } else {
+ write_extent_buffer(buf, result, 0, csum_size);
+ }
+ return 0;
+}
+
+int csum_tree_block_size(struct extent_buffer *buf, u16 csum_size, int verify,
+ u16 csum_type)
+{
+ return __csum_tree_block_size(buf, csum_size, verify, 0, csum_type);
+}
+
+static int csum_tree_block(struct btrfs_fs_info *fs_info,
+ struct extent_buffer *buf, int verify)
+{
+ u16 csum_size = btrfs_super_csum_size(fs_info->super_copy);
+ u16 csum_type = btrfs_super_csum_type(fs_info->super_copy);
+
+ return csum_tree_block_size(buf, csum_size, verify, csum_type);
+}
+
+struct extent_buffer *btrfs_find_tree_block(struct btrfs_fs_info *fs_info,
+ u64 bytenr, u32 blocksize)
+{
+ return find_extent_buffer(&fs_info->extent_cache,
+ bytenr, blocksize);
+}
+
+struct extent_buffer* btrfs_find_create_tree_block(
+ struct btrfs_fs_info *fs_info, u64 bytenr)
+{
+ return alloc_extent_buffer(fs_info, bytenr, fs_info->nodesize);
+}
+
+static int verify_parent_transid(struct extent_io_tree *io_tree,
+ struct extent_buffer *eb, u64 parent_transid,
+ int ignore)
+{
+ int ret;
+
+ if (!parent_transid || btrfs_header_generation(eb) == parent_transid)
+ return 0;
+
+ if (extent_buffer_uptodate(eb) &&
+ btrfs_header_generation(eb) == parent_transid) {
+ ret = 0;
+ goto out;
+ }
+ printk("parent transid verify failed on %llu wanted %llu found %llu\n",
+ (unsigned long long)eb->start,
+ (unsigned long long)parent_transid,
+ (unsigned long long)btrfs_header_generation(eb));
+ if (ignore) {
+ eb->flags |= EXTENT_BAD_TRANSID;
+ printk("Ignoring transid failure\n");
+ return 0;
+ }
+
+ ret = 1;
+out:
+ clear_extent_buffer_uptodate(eb);
+ return ret;
+
+}
+
+int read_whole_eb(struct btrfs_fs_info *info, struct extent_buffer *eb, int mirror)
+{
+ unsigned long offset = 0;
+ struct btrfs_multi_bio *multi = NULL;
+ struct btrfs_device *device;
+ int ret = 0;
+ u64 read_len;
+ unsigned long bytes_left = eb->len;
+
+ while (bytes_left) {
+ read_len = bytes_left;
+ device = NULL;
+
+ ret = btrfs_map_block(info, READ, eb->start + offset,
+ &read_len, &multi, mirror, NULL);
+ if (ret) {
+ printk("Couldn't map the block %Lu\n", eb->start + offset);
+ kfree(multi);
+ return -EIO;
+ }
+ device = multi->stripes[0].dev;
+
+ if (!device->desc || !device->part) {
+ kfree(multi);
+ return -EIO;
+ }
+
+ if (read_len > bytes_left)
+ read_len = bytes_left;
+
+ ret = read_extent_from_disk(device->desc, device->part,
+ multi->stripes[0].physical, eb,
+ offset, read_len);
+ kfree(multi);
+ multi = NULL;
+
+ if (ret)
+ return -EIO;
+ offset += read_len;
+ bytes_left -= read_len;
+ }
+ return 0;
+}
+
+struct extent_buffer* read_tree_block(struct btrfs_fs_info *fs_info, u64 bytenr,
+ u64 parent_transid)
+{
+ int ret;
+ struct extent_buffer *eb;
+ u64 best_transid = 0;
+ u32 sectorsize = fs_info->sectorsize;
+ int mirror_num = 1;
+ int good_mirror = 0;
+ int candidate_mirror = 0;
+ int num_copies;
+ int ignore = 0;
+
+ /*
+ * Don't even try to create tree block for unaligned tree block
+ * bytenr.
+ * Such unaligned tree block will free overlapping extent buffer,
+ * causing use-after-free bugs for fuzzed images.
+ */
+ if (bytenr < sectorsize || !IS_ALIGNED(bytenr, sectorsize)) {
+ error("tree block bytenr %llu is not aligned to sectorsize %u",
+ bytenr, sectorsize);
+ return ERR_PTR(-EIO);
+ }
+
+ eb = btrfs_find_create_tree_block(fs_info, bytenr);
+ if (!eb)
+ return ERR_PTR(-ENOMEM);
+
+ if (btrfs_buffer_uptodate(eb, parent_transid))
+ return eb;
+
+ num_copies = btrfs_num_copies(fs_info, eb->start, eb->len);
+ while (1) {
+ ret = read_whole_eb(fs_info, eb, mirror_num);
+ if (ret == 0 && csum_tree_block(fs_info, eb, 1) == 0 &&
+ check_tree_block(fs_info, eb) == 0 &&
+ verify_parent_transid(&fs_info->extent_cache, eb,
+ parent_transid, ignore) == 0) {
+ /*
+ * check_tree_block() is less strict to allow btrfs
+ * check to get raw eb with bad key order and fix it.
+ * But we still need to try to get a good copy if
+ * possible, or bad key order can go into tools like
+ * btrfs ins dump-tree.
+ */
+ if (btrfs_header_level(eb))
+ ret = btrfs_check_node(fs_info, NULL, eb);
+ else
+ ret = btrfs_check_leaf(fs_info, NULL, eb);
+ if (!ret || candidate_mirror == mirror_num) {
+ btrfs_set_buffer_uptodate(eb);
+ return eb;
+ }
+ if (candidate_mirror <= 0)
+ candidate_mirror = mirror_num;
+ }
+ if (ignore) {
+ if (candidate_mirror > 0) {
+ mirror_num = candidate_mirror;
+ continue;
+ }
+ if (check_tree_block(fs_info, eb))
+ print_tree_block_error(fs_info, eb,
+ check_tree_block(fs_info, eb));
+ else
+ fprintf(stderr, "Csum didn't match\n");
+ ret = -EIO;
+ break;
+ }
+ if (num_copies == 1) {
+ ignore = 1;
+ continue;
+ }
+ if (btrfs_header_generation(eb) > best_transid) {
+ best_transid = btrfs_header_generation(eb);
+ good_mirror = mirror_num;
+ }
+ mirror_num++;
+ if (mirror_num > num_copies) {
+ if (candidate_mirror > 0)
+ mirror_num = candidate_mirror;
+ else
+ mirror_num = good_mirror;
+ ignore = 1;
+ continue;
+ }
+ }
+ /*
+ * We failed to read this tree block, it be should deleted right now
+ * to avoid stale cache populate the cache.
+ */
+ free_extent_buffer(eb);
+ return ERR_PTR(ret);
+}
+
+int read_extent_data(struct btrfs_fs_info *fs_info, char *data, u64 logical,
+ u64 *len, int mirror)
+{
+ u64 offset = 0;
+ struct btrfs_multi_bio *multi = NULL;
+ struct btrfs_device *device;
+ int ret = 0;
+ u64 max_len = *len;
+
+ ret = btrfs_map_block(fs_info, READ, logical, len, &multi, mirror,
+ NULL);
+ if (ret) {
+ fprintf(stderr, "Couldn't map the block %llu\n",
+ logical + offset);
+ goto err;
+ }
+ device = multi->stripes[0].dev;
+
+ if (*len > max_len)
+ *len = max_len;
+ if (!device->desc || !device->part) {
+ ret = -EIO;
+ goto err;
+ }
+
+ ret = __btrfs_devread(device->desc, device->part, data, *len,
+ multi->stripes[0].physical);
+ if (ret != *len)
+ ret = -EIO;
+ else
+ ret = 0;
+err:
+ kfree(multi);
+ return ret;
+}
+
+void btrfs_setup_root(struct btrfs_root *root, struct btrfs_fs_info *fs_info,
+ u64 objectid)
+{
+ root->node = NULL;
+ root->track_dirty = 0;
+
+ root->fs_info = fs_info;
+ root->objectid = objectid;
+ root->last_trans = 0;
+ root->last_inode_alloc = 0;
+
+ memset(&root->root_key, 0, sizeof(root->root_key));
+ memset(&root->root_item, 0, sizeof(root->root_item));
+ root->root_key.objectid = objectid;
+}
+
+static int find_and_setup_root(struct btrfs_root *tree_root,
+ struct btrfs_fs_info *fs_info,
+ u64 objectid, struct btrfs_root *root)
+{
+ int ret;
+ u64 generation;
+
+ btrfs_setup_root(root, fs_info, objectid);
+ ret = btrfs_find_last_root(tree_root, objectid,
+ &root->root_item, &root->root_key);
+ if (ret)
+ return ret;
+
+ generation = btrfs_root_generation(&root->root_item);
+ root->node = read_tree_block(fs_info,
+ btrfs_root_bytenr(&root->root_item), generation);
+ if (!extent_buffer_uptodate(root->node))
+ return -EIO;
+
+ return 0;
+}
+
+int btrfs_free_fs_root(struct btrfs_root *root)
+{
+ if (root->node)
+ free_extent_buffer(root->node);
+ kfree(root);
+ return 0;
+}
+
+static void __free_fs_root(struct rb_node *node)
+{
+ struct btrfs_root *root;
+
+ root = container_of(node, struct btrfs_root, rb_node);
+ btrfs_free_fs_root(root);
+}
+
+FREE_RB_BASED_TREE(fs_roots, __free_fs_root);
+
+struct btrfs_root *btrfs_read_fs_root_no_cache(struct btrfs_fs_info *fs_info,
+ struct btrfs_key *location)
+{
+ struct btrfs_root *root;
+ struct btrfs_root *tree_root = fs_info->tree_root;
+ struct btrfs_path *path;
+ struct extent_buffer *l;
+ u64 generation;
+ int ret = 0;
+
+ root = calloc(1, sizeof(*root));
+ if (!root)
+ return ERR_PTR(-ENOMEM);
+ if (location->offset == (u64)-1) {
+ ret = find_and_setup_root(tree_root, fs_info,
+ location->objectid, root);
+ if (ret) {
+ free(root);
+ return ERR_PTR(ret);
+ }
+ goto insert;
+ }
+
+ btrfs_setup_root(root, fs_info,
+ location->objectid);
+
+ path = btrfs_alloc_path();
+ if (!path) {
+ free(root);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ ret = btrfs_search_slot(NULL, tree_root, location, path, 0, 0);
+ if (ret != 0) {
+ if (ret > 0)
+ ret = -ENOENT;
+ goto out;
+ }
+ l = path->nodes[0];
+ read_extent_buffer(l, &root->root_item,
+ btrfs_item_ptr_offset(l, path->slots[0]),
+ sizeof(root->root_item));
+ memcpy(&root->root_key, location, sizeof(*location));
+
+ /* If this root is already an orphan, no need to read */
+ if (btrfs_root_refs(&root->root_item) == 0) {
+ ret = -ENOENT;
+ goto out;
+ }
+ ret = 0;
+out:
+ btrfs_free_path(path);
+ if (ret) {
+ free(root);
+ return ERR_PTR(ret);
+ }
+ generation = btrfs_root_generation(&root->root_item);
+ root->node = read_tree_block(fs_info,
+ btrfs_root_bytenr(&root->root_item), generation);
+ if (!extent_buffer_uptodate(root->node)) {
+ free(root);
+ return ERR_PTR(-EIO);
+ }
+insert:
+ root->ref_cows = 1;
+ return root;
+}
+
+static int btrfs_fs_roots_compare_objectids(struct rb_node *node,
+ void *data)
+{
+ u64 objectid = *((u64 *)data);
+ struct btrfs_root *root;
+
+ root = rb_entry(node, struct btrfs_root, rb_node);
+ if (objectid > root->objectid)
+ return 1;
+ else if (objectid < root->objectid)
+ return -1;
+ else
+ return 0;
+}
+
+int btrfs_fs_roots_compare_roots(struct rb_node *node1, struct rb_node *node2)
+{
+ struct btrfs_root *root;
+
+ root = rb_entry(node2, struct btrfs_root, rb_node);
+ return btrfs_fs_roots_compare_objectids(node1, (void *)&root->objectid);
+}
+
+struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info,
+ struct btrfs_key *location)
+{
+ struct btrfs_root *root;
+ struct rb_node *node;
+ int ret;
+ u64 objectid = location->objectid;
+
+ if (location->objectid == BTRFS_ROOT_TREE_OBJECTID)
+ return fs_info->tree_root;
+ if (location->objectid == BTRFS_CHUNK_TREE_OBJECTID)
+ return fs_info->chunk_root;
+ if (location->objectid == BTRFS_CSUM_TREE_OBJECTID)
+ return fs_info->csum_root;
+ BUG_ON(location->objectid == BTRFS_TREE_RELOC_OBJECTID);
+
+ node = rb_search(&fs_info->fs_root_tree, (void *)&objectid,
+ btrfs_fs_roots_compare_objectids, NULL);
+ if (node)
+ return container_of(node, struct btrfs_root, rb_node);
+
+ root = btrfs_read_fs_root_no_cache(fs_info, location);
+ if (IS_ERR(root))
+ return root;
+
+ ret = rb_insert(&fs_info->fs_root_tree, &root->rb_node,
+ btrfs_fs_roots_compare_roots);
+ BUG_ON(ret);
+ return root;
+}
+
+void btrfs_free_fs_info(struct btrfs_fs_info *fs_info)
+{
+ free(fs_info->tree_root);
+ free(fs_info->chunk_root);
+ free(fs_info->csum_root);
+ free(fs_info->super_copy);
+ free(fs_info);
+}
+
+struct btrfs_fs_info *btrfs_new_fs_info(void)
+{
+ struct btrfs_fs_info *fs_info;
+
+ fs_info = calloc(1, sizeof(struct btrfs_fs_info));
+ if (!fs_info)
+ return NULL;
+
+ fs_info->tree_root = calloc(1, sizeof(struct btrfs_root));
+ fs_info->chunk_root = calloc(1, sizeof(struct btrfs_root));
+ fs_info->csum_root = calloc(1, sizeof(struct btrfs_root));
+ fs_info->super_copy = calloc(1, BTRFS_SUPER_INFO_SIZE);
+
+ if (!fs_info->tree_root || !fs_info->chunk_root ||
+ !fs_info->csum_root || !fs_info->super_copy)
+ goto free_all;
+
+ extent_io_tree_init(&fs_info->extent_cache);
+
+ fs_info->fs_root_tree = RB_ROOT;
+ cache_tree_init(&fs_info->mapping_tree.cache_tree);
+
+ mutex_init(&fs_info->fs_mutex);
+
+ return fs_info;
+free_all:
+ btrfs_free_fs_info(fs_info);
+ return NULL;
+}
+
+static int setup_root_or_create_block(struct btrfs_fs_info *fs_info,
+ struct btrfs_root *info_root,
+ u64 objectid, char *str)
+{
+ struct btrfs_root *root = fs_info->tree_root;
+ int ret;
+
+ ret = find_and_setup_root(root, fs_info, objectid, info_root);
+ if (ret) {
+ error("could not setup %s tree", str);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info)
+{
+ struct btrfs_super_block *sb = fs_info->super_copy;
+ struct btrfs_root *root;
+ struct btrfs_key key;
+ u64 root_tree_bytenr;
+ u64 generation;
+ int ret;
+
+ root = fs_info->tree_root;
+ btrfs_setup_root(root, fs_info, BTRFS_ROOT_TREE_OBJECTID);
+ generation = btrfs_super_generation(sb);
+
+ root_tree_bytenr = btrfs_super_root(sb);
+
+ root->node = read_tree_block(fs_info, root_tree_bytenr, generation);
+ if (!extent_buffer_uptodate(root->node)) {
+ fprintf(stderr, "Couldn't read tree root\n");
+ return -EIO;
+ }
+
+ ret = setup_root_or_create_block(fs_info, fs_info->csum_root,
+ BTRFS_CSUM_TREE_OBJECTID, "csum");
+ if (ret)
+ return ret;
+ fs_info->csum_root->track_dirty = 1;
+
+ fs_info->last_trans_committed = generation;
+
+ key.objectid = BTRFS_FS_TREE_OBJECTID;
+ key.type = BTRFS_ROOT_ITEM_KEY;
+ key.offset = (u64)-1;
+ fs_info->fs_root = btrfs_read_fs_root(fs_info, &key);
+
+ if (IS_ERR(fs_info->fs_root))
+ return -EIO;
+ return 0;
+}
+
+void btrfs_release_all_roots(struct btrfs_fs_info *fs_info)
+{
+ if (fs_info->csum_root)
+ free_extent_buffer(fs_info->csum_root->node);
+ if (fs_info->tree_root)
+ free_extent_buffer(fs_info->tree_root->node);
+ if (fs_info->chunk_root)
+ free_extent_buffer(fs_info->chunk_root->node);
+}
+
+static void free_map_lookup(struct cache_extent *ce)
+{
+ struct map_lookup *map;
+
+ map = container_of(ce, struct map_lookup, ce);
+ kfree(map);
+}
+
+FREE_EXTENT_CACHE_BASED_TREE(mapping_cache, free_map_lookup);
+
+void btrfs_cleanup_all_caches(struct btrfs_fs_info *fs_info)
+{
+ free_mapping_cache_tree(&fs_info->mapping_tree.cache_tree);
+ extent_io_tree_cleanup(&fs_info->extent_cache);
+}
+
+static int btrfs_scan_fs_devices(struct blk_desc *desc,
+ struct disk_partition *part,
+ struct btrfs_fs_devices **fs_devices)
+{
+ u64 total_devs;
+ int ret;
+
+ if (round_up(BTRFS_SUPER_INFO_SIZE + BTRFS_SUPER_INFO_OFFSET,
+ desc->blksz) > (part->size << desc->log2blksz)) {
+ error("superblock end %u is larger than device size " LBAFU,
+ BTRFS_SUPER_INFO_SIZE + BTRFS_SUPER_INFO_OFFSET,
+ part->size << desc->log2blksz);
+ return -EINVAL;
+ }
+
+ ret = btrfs_scan_one_device(desc, part, fs_devices, &total_devs);
+ if (ret) {
+ fprintf(stderr, "No valid Btrfs found\n");
+ return ret;
+ }
+ return 0;
+}
+
+int btrfs_check_fs_compatibility(struct btrfs_super_block *sb)
+{
+ u64 features;
+
+ features = btrfs_super_incompat_flags(sb) &
+ ~BTRFS_FEATURE_INCOMPAT_SUPP;
+ if (features) {
+ printk("couldn't open because of unsupported "
+ "option features (%llx).\n",
+ (unsigned long long)features);
+ return -ENOTSUPP;
+ }
+
+ features = btrfs_super_incompat_flags(sb);
+ if (!(features & BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF)) {
+ features |= BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF;
+ btrfs_set_super_incompat_flags(sb, features);
+ }
+
+ return 0;
+}
+
+static int btrfs_setup_chunk_tree_and_device_map(struct btrfs_fs_info *fs_info)
+{
+ struct btrfs_super_block *sb = fs_info->super_copy;
+ u64 chunk_root_bytenr;
+ u64 generation;
+ int ret;
+
+ btrfs_setup_root(fs_info->chunk_root, fs_info,
+ BTRFS_CHUNK_TREE_OBJECTID);
+
+ ret = btrfs_read_sys_array(fs_info);
+ if (ret)
+ return ret;
+
+ generation = btrfs_super_chunk_root_generation(sb);
+ chunk_root_bytenr = btrfs_super_chunk_root(sb);
+
+ fs_info->chunk_root->node = read_tree_block(fs_info,
+ chunk_root_bytenr,
+ generation);
+ if (!extent_buffer_uptodate(fs_info->chunk_root->node)) {
+ error("cannot read chunk root");
+ return -EIO;
+ }
+
+ ret = btrfs_read_chunk_tree(fs_info);
+ if (ret) {
+ fprintf(stderr, "Couldn't read chunk tree\n");
+ return ret;
+ }
+ return 0;
+}
+
+struct btrfs_fs_info *open_ctree_fs_info(struct blk_desc *desc,
+ struct disk_partition *part)
+{
+ struct btrfs_fs_info *fs_info;
+ struct btrfs_super_block *disk_super;
+ struct btrfs_fs_devices *fs_devices = NULL;
+ struct extent_buffer *eb;
+ int ret;
+
+ fs_info = btrfs_new_fs_info();
+ if (!fs_info) {
+ fprintf(stderr, "Failed to allocate memory for fs_info\n");
+ return NULL;
+ }
+
+ ret = btrfs_scan_fs_devices(desc, part, &fs_devices);
+ if (ret)
+ goto out;
+
+ fs_info->fs_devices = fs_devices;
+
+ ret = btrfs_open_devices(fs_devices);
+ if (ret)
+ goto out;
+
+ disk_super = fs_info->super_copy;
+ ret = btrfs_read_dev_super(desc, part, disk_super);
+ if (ret) {
+ printk("No valid btrfs found\n");
+ goto out_devices;
+ }
+
+ if (btrfs_super_flags(disk_super) & BTRFS_SUPER_FLAG_CHANGING_FSID) {
+ fprintf(stderr, "ERROR: Filesystem UUID change in progress\n");
+ goto out_devices;
+ }
+
+ ASSERT(!memcmp(disk_super->fsid, fs_devices->fsid, BTRFS_FSID_SIZE));
+ if (btrfs_fs_incompat(fs_info, METADATA_UUID))
+ ASSERT(!memcmp(disk_super->metadata_uuid,
+ fs_devices->metadata_uuid, BTRFS_FSID_SIZE));
+
+ fs_info->sectorsize = btrfs_super_sectorsize(disk_super);
+ fs_info->nodesize = btrfs_super_nodesize(disk_super);
+ fs_info->stripesize = btrfs_super_stripesize(disk_super);
+
+ ret = btrfs_check_fs_compatibility(fs_info->super_copy);
+ if (ret)
+ goto out_devices;
+
+ ret = btrfs_setup_chunk_tree_and_device_map(fs_info);
+ if (ret)
+ goto out_chunk;
+
+ /* Chunk tree root is unable to read, return directly */
+ if (!fs_info->chunk_root)
+ return fs_info;
+
+ eb = fs_info->chunk_root->node;
+ read_extent_buffer(eb, fs_info->chunk_tree_uuid,
+ btrfs_header_chunk_tree_uuid(eb),
+ BTRFS_UUID_SIZE);
+
+ ret = btrfs_setup_all_roots(fs_info);
+ if (ret)
+ goto out_chunk;
+
+ return fs_info;
+
+out_chunk:
+ btrfs_release_all_roots(fs_info);
+ btrfs_cleanup_all_caches(fs_info);
+out_devices:
+ btrfs_close_devices(fs_devices);
+out:
+ btrfs_free_fs_info(fs_info);
+ return NULL;
+}
+
+int close_ctree_fs_info(struct btrfs_fs_info *fs_info)
+{
+ int ret;
+
+ free_fs_roots_tree(&fs_info->fs_root_tree);
+
+ btrfs_release_all_roots(fs_info);
+ ret = btrfs_close_devices(fs_info->fs_devices);
+ btrfs_cleanup_all_caches(fs_info);
+ btrfs_free_fs_info(fs_info);
+ return ret;
+}
+
+int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid)
+{
+ int ret;
+
+ ret = extent_buffer_uptodate(buf);
+ if (!ret)
+ return ret;
+
+ ret = verify_parent_transid(&buf->fs_info->extent_cache, buf,
+ parent_transid, 1);
+ return !ret;
+}
+
+int btrfs_set_buffer_uptodate(struct extent_buffer *eb)
+{
+ return set_extent_buffer_uptodate(eb);
+}
diff --git a/roms/u-boot/fs/btrfs/disk-io.h b/roms/u-boot/fs/btrfs/disk-io.h
new file mode 100644
index 000000000..a34791207
--- /dev/null
+++ b/roms/u-boot/fs/btrfs/disk-io.h
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: GPL-2.0+
+#ifndef __BTRFS_DISK_IO_H__
+#define __BTRFS_DISK_IO_H__
+
+#include <linux/sizes.h>
+#include <fs_internal.h>
+#include "ctree.h"
+#include "disk-io.h"
+
+#define BTRFS_SUPER_INFO_OFFSET SZ_64K
+#define BTRFS_SUPER_INFO_SIZE SZ_4K
+
+/* From btrfs-progs */
+int read_whole_eb(struct btrfs_fs_info *info, struct extent_buffer *eb, int mirror);
+struct extent_buffer* read_tree_block(struct btrfs_fs_info *fs_info, u64 bytenr,
+ u64 parent_transid);
+
+int read_extent_data(struct btrfs_fs_info *fs_info, char *data, u64 logical,
+ u64 *len, int mirror);
+struct extent_buffer* btrfs_find_create_tree_block(
+ struct btrfs_fs_info *fs_info, u64 bytenr);
+struct extent_buffer *btrfs_find_tree_block(struct btrfs_fs_info *fs_info,
+ u64 bytenr, u32 blocksize);
+struct btrfs_root *btrfs_read_fs_root_no_cache(struct btrfs_fs_info *fs_info,
+ struct btrfs_key *location);
+struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info,
+ struct btrfs_key *location);
+
+void btrfs_setup_root(struct btrfs_root *root, struct btrfs_fs_info *fs_info,
+ u64 objectid);
+
+void btrfs_free_fs_info(struct btrfs_fs_info *fs_info);
+struct btrfs_fs_info *btrfs_new_fs_info(void);
+int btrfs_check_fs_compatibility(struct btrfs_super_block *sb);
+int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info);
+void btrfs_release_all_roots(struct btrfs_fs_info *fs_info);
+void btrfs_cleanup_all_caches(struct btrfs_fs_info *fs_info);
+
+struct btrfs_fs_info *open_ctree_fs_info(struct blk_desc *desc,
+ struct disk_partition *part);
+int close_ctree_fs_info(struct btrfs_fs_info *fs_info);
+
+int btrfs_read_dev_super(struct blk_desc *desc, struct disk_partition *part,
+ struct btrfs_super_block *sb);
+int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid);
+int btrfs_set_buffer_uptodate(struct extent_buffer *buf);
+int btrfs_csum_data(u16 csum_type, const u8 *data, u8 *out, size_t len);
+int csum_tree_block_size(struct extent_buffer *buf, u16 csum_sectorsize,
+ int verify, u16 csum_type);
+#endif
diff --git a/roms/u-boot/fs/btrfs/extent-cache.c b/roms/u-boot/fs/btrfs/extent-cache.c
new file mode 100644
index 000000000..bc8cf3a52
--- /dev/null
+++ b/roms/u-boot/fs/btrfs/extent-cache.c
@@ -0,0 +1,318 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/*
+ * Crossported from the same named file of btrfs-progs.
+ *
+ * Minor modification to include headers.
+ */
+#include <linux/kernel.h>
+#include <linux/rbtree.h>
+#include <linux/errno.h>
+#include <linux/bug.h>
+#include <stdlib.h>
+#include "extent-cache.h"
+#include "common/rbtree-utils.h"
+
+struct cache_extent_search_range {
+ u64 objectid;
+ u64 start;
+ u64 size;
+};
+
+static int cache_tree_comp_range(struct rb_node *node, void *data)
+{
+ struct cache_extent *entry;
+ struct cache_extent_search_range *range;
+
+ range = (struct cache_extent_search_range *)data;
+ entry = rb_entry(node, struct cache_extent, rb_node);
+
+ if (entry->start + entry->size <= range->start)
+ return 1;
+ else if (range->start + range->size <= entry->start)
+ return -1;
+ else
+ return 0;
+}
+
+static int cache_tree_comp_nodes(struct rb_node *node1, struct rb_node *node2)
+{
+ struct cache_extent *entry;
+ struct cache_extent_search_range range;
+
+ entry = rb_entry(node2, struct cache_extent, rb_node);
+ range.start = entry->start;
+ range.size = entry->size;
+
+ return cache_tree_comp_range(node1, (void *)&range);
+}
+
+static int cache_tree_comp_range2(struct rb_node *node, void *data)
+{
+ struct cache_extent *entry;
+ struct cache_extent_search_range *range;
+
+ range = (struct cache_extent_search_range *)data;
+ entry = rb_entry(node, struct cache_extent, rb_node);
+
+ if (entry->objectid < range->objectid)
+ return 1;
+ else if (entry->objectid > range->objectid)
+ return -1;
+ else if (entry->start + entry->size <= range->start)
+ return 1;
+ else if (range->start + range->size <= entry->start)
+ return -1;
+ else
+ return 0;
+}
+
+static int cache_tree_comp_nodes2(struct rb_node *node1, struct rb_node *node2)
+{
+ struct cache_extent *entry;
+ struct cache_extent_search_range range;
+
+ entry = rb_entry(node2, struct cache_extent, rb_node);
+ range.objectid = entry->objectid;
+ range.start = entry->start;
+ range.size = entry->size;
+
+ return cache_tree_comp_range2(node1, (void *)&range);
+}
+
+void cache_tree_init(struct cache_tree *tree)
+{
+ tree->root = RB_ROOT;
+}
+
+static struct cache_extent *alloc_cache_extent(u64 start, u64 size)
+{
+ struct cache_extent *pe = malloc(sizeof(*pe));
+
+ if (!pe)
+ return pe;
+
+ pe->objectid = 0;
+ pe->start = start;
+ pe->size = size;
+ return pe;
+}
+
+int add_cache_extent(struct cache_tree *tree, u64 start, u64 size)
+{
+ struct cache_extent *pe = alloc_cache_extent(start, size);
+ int ret;
+
+ if (!pe)
+ return -ENOMEM;
+
+ ret = insert_cache_extent(tree, pe);
+ if (ret)
+ free(pe);
+
+ return ret;
+}
+
+int insert_cache_extent(struct cache_tree *tree, struct cache_extent *pe)
+{
+ return rb_insert(&tree->root, &pe->rb_node, cache_tree_comp_nodes);
+}
+
+int insert_cache_extent2(struct cache_tree *tree, struct cache_extent *pe)
+{
+ return rb_insert(&tree->root, &pe->rb_node, cache_tree_comp_nodes2);
+}
+
+struct cache_extent *lookup_cache_extent(struct cache_tree *tree,
+ u64 start, u64 size)
+{
+ struct rb_node *node;
+ struct cache_extent *entry;
+ struct cache_extent_search_range range;
+
+ range.start = start;
+ range.size = size;
+ node = rb_search(&tree->root, &range, cache_tree_comp_range, NULL);
+ if (!node)
+ return NULL;
+
+ entry = rb_entry(node, struct cache_extent, rb_node);
+ return entry;
+}
+
+struct cache_extent *lookup_cache_extent2(struct cache_tree *tree,
+ u64 objectid, u64 start, u64 size)
+{
+ struct rb_node *node;
+ struct cache_extent *entry;
+ struct cache_extent_search_range range;
+
+ range.objectid = objectid;
+ range.start = start;
+ range.size = size;
+ node = rb_search(&tree->root, &range, cache_tree_comp_range2, NULL);
+ if (!node)
+ return NULL;
+
+ entry = rb_entry(node, struct cache_extent, rb_node);
+ return entry;
+}
+
+struct cache_extent *search_cache_extent(struct cache_tree *tree, u64 start)
+{
+ struct rb_node *next;
+ struct rb_node *node;
+ struct cache_extent *entry;
+ struct cache_extent_search_range range;
+
+ range.start = start;
+ range.size = 1;
+ node = rb_search(&tree->root, &range, cache_tree_comp_range, &next);
+ if (!node)
+ node = next;
+ if (!node)
+ return NULL;
+
+ entry = rb_entry(node, struct cache_extent, rb_node);
+ return entry;
+}
+
+struct cache_extent *search_cache_extent2(struct cache_tree *tree,
+ u64 objectid, u64 start)
+{
+ struct rb_node *next;
+ struct rb_node *node;
+ struct cache_extent *entry;
+ struct cache_extent_search_range range;
+
+ range.objectid = objectid;
+ range.start = start;
+ range.size = 1;
+ node = rb_search(&tree->root, &range, cache_tree_comp_range2, &next);
+ if (!node)
+ node = next;
+ if (!node)
+ return NULL;
+
+ entry = rb_entry(node, struct cache_extent, rb_node);
+ return entry;
+}
+
+struct cache_extent *first_cache_extent(struct cache_tree *tree)
+{
+ struct rb_node *node = rb_first(&tree->root);
+
+ if (!node)
+ return NULL;
+ return rb_entry(node, struct cache_extent, rb_node);
+}
+
+struct cache_extent *last_cache_extent(struct cache_tree *tree)
+{
+ struct rb_node *node = rb_last(&tree->root);
+
+ if (!node)
+ return NULL;
+ return rb_entry(node, struct cache_extent, rb_node);
+}
+
+struct cache_extent *prev_cache_extent(struct cache_extent *pe)
+{
+ struct rb_node *node = rb_prev(&pe->rb_node);
+
+ if (!node)
+ return NULL;
+ return rb_entry(node, struct cache_extent, rb_node);
+}
+
+struct cache_extent *next_cache_extent(struct cache_extent *pe)
+{
+ struct rb_node *node = rb_next(&pe->rb_node);
+
+ if (!node)
+ return NULL;
+ return rb_entry(node, struct cache_extent, rb_node);
+}
+
+void remove_cache_extent(struct cache_tree *tree, struct cache_extent *pe)
+{
+ rb_erase(&pe->rb_node, &tree->root);
+}
+
+void cache_tree_free_extents(struct cache_tree *tree,
+ free_cache_extent free_func)
+{
+ struct cache_extent *ce;
+
+ while ((ce = first_cache_extent(tree))) {
+ remove_cache_extent(tree, ce);
+ free_func(ce);
+ }
+}
+
+static void free_extent_cache(struct cache_extent *pe)
+{
+ free(pe);
+}
+
+void free_extent_cache_tree(struct cache_tree *tree)
+{
+ cache_tree_free_extents(tree, free_extent_cache);
+}
+
+int add_merge_cache_extent(struct cache_tree *tree, u64 start, u64 size)
+{
+ struct cache_extent *cache;
+ struct cache_extent *next = NULL;
+ struct cache_extent *prev = NULL;
+ int next_merged = 0;
+ int prev_merged = 0;
+ int ret = 0;
+
+ if (cache_tree_empty(tree))
+ goto insert;
+
+ cache = search_cache_extent(tree, start);
+ if (!cache) {
+ /*
+ * Either the tree is completely empty, or the no range after
+ * start.
+ * Either way, the last cache_extent should be prev.
+ */
+ prev = last_cache_extent(tree);
+ } else if (start <= cache->start) {
+ next = cache;
+ prev = prev_cache_extent(cache);
+ } else {
+ prev = cache;
+ next = next_cache_extent(cache);
+ }
+
+ /*
+ * Ensure the range to be inserted won't cover with existings
+ * Or we will need extra loop to do merge
+ */
+ BUG_ON(next && start + size > next->start);
+ BUG_ON(prev && prev->start + prev->size > start);
+
+ if (next && start + size == next->start) {
+ next_merged = 1;
+ next->size = next->start + next->size - start;
+ next->start = start;
+ }
+ if (prev && prev->start + prev->size == start) {
+ prev_merged = 1;
+ if (next_merged) {
+ next->size = next->start + next->size - prev->start;
+ next->start = prev->start;
+ remove_cache_extent(tree, prev);
+ free(prev);
+ } else {
+ prev->size = start + size - prev->start;
+ }
+ }
+insert:
+ if (!prev_merged && !next_merged)
+ ret = add_cache_extent(tree, start, size);
+ return ret;
+}
diff --git a/roms/u-boot/fs/btrfs/extent-cache.h b/roms/u-boot/fs/btrfs/extent-cache.h
new file mode 100644
index 000000000..2fee81a66
--- /dev/null
+++ b/roms/u-boot/fs/btrfs/extent-cache.h
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/*
+ * Crossported from the same named file of btrfs-progs.
+ *
+ * Minor modification to include headers.
+ */
+#ifndef __BTRFS_EXTENT_CACHE_H__
+#define __BTRFS_EXTENT_CACHE_H__
+
+#include <linux/rbtree.h>
+#include <linux/types.h>
+
+struct cache_tree {
+ struct rb_root root;
+};
+
+struct cache_extent {
+ struct rb_node rb_node;
+ u64 objectid;
+ u64 start;
+ u64 size;
+};
+
+void cache_tree_init(struct cache_tree *tree);
+
+struct cache_extent *first_cache_extent(struct cache_tree *tree);
+struct cache_extent *last_cache_extent(struct cache_tree *tree);
+struct cache_extent *prev_cache_extent(struct cache_extent *pe);
+struct cache_extent *next_cache_extent(struct cache_extent *pe);
+
+/*
+ * Find a cache_extent which covers start.
+ *
+ * If not found, return next cache_extent if possible.
+ */
+struct cache_extent *search_cache_extent(struct cache_tree *tree, u64 start);
+
+/*
+ * Find a cache_extent which restrictly covers start.
+ *
+ * If not found, return NULL.
+ */
+struct cache_extent *lookup_cache_extent(struct cache_tree *tree,
+ u64 start, u64 size);
+
+/*
+ * Add an non-overlap extent into cache tree
+ *
+ * If [start, start+size) overlap with existing one, it will return -EEXIST.
+ */
+int add_cache_extent(struct cache_tree *tree, u64 start, u64 size);
+
+/*
+ * Same with add_cache_extent, but with cache_extent strcut.
+ */
+int insert_cache_extent(struct cache_tree *tree, struct cache_extent *pe);
+void remove_cache_extent(struct cache_tree *tree, struct cache_extent *pe);
+
+static inline int cache_tree_empty(struct cache_tree *tree)
+{
+ return RB_EMPTY_ROOT(&tree->root);
+}
+
+typedef void (*free_cache_extent)(struct cache_extent *pe);
+
+void cache_tree_free_extents(struct cache_tree *tree,
+ free_cache_extent free_func);
+
+#define FREE_EXTENT_CACHE_BASED_TREE(name, free_func) \
+static void free_##name##_tree(struct cache_tree *tree) \
+{ \
+ cache_tree_free_extents(tree, free_func); \
+}
+
+void free_extent_cache_tree(struct cache_tree *tree);
+
+/*
+ * Search a cache_extent with same objectid, and covers start.
+ *
+ * If not found, return next if possible.
+ */
+struct cache_extent *search_cache_extent2(struct cache_tree *tree,
+ u64 objectid, u64 start);
+/*
+ * Search a cache_extent with same objectid, and covers the range
+ * [start, start + size)
+ *
+ * If not found, return next cache_extent if possible.
+ */
+struct cache_extent *lookup_cache_extent2(struct cache_tree *tree,
+ u64 objectid, u64 start, u64 size);
+int insert_cache_extent2(struct cache_tree *tree, struct cache_extent *pe);
+
+/*
+ * Insert a cache_extent range [start, start + size).
+ *
+ * This function may merge with existing cache_extent.
+ * NOTE: caller must ensure the inserted range won't cover with any existing
+ * range.
+ */
+int add_merge_cache_extent(struct cache_tree *tree, u64 start, u64 size);
+
+#endif
diff --git a/roms/u-boot/fs/btrfs/extent-io.c b/roms/u-boot/fs/btrfs/extent-io.c
new file mode 100644
index 000000000..774e29eb6
--- /dev/null
+++ b/roms/u-boot/fs/btrfs/extent-io.c
@@ -0,0 +1,809 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * BTRFS filesystem implementation for U-Boot
+ *
+ * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz
+ */
+
+#include <linux/kernel.h>
+#include <linux/bug.h>
+#include <malloc.h>
+#include <memalign.h>
+#include "btrfs.h"
+#include "ctree.h"
+#include "extent-io.h"
+#include "disk-io.h"
+
+void extent_io_tree_init(struct extent_io_tree *tree)
+{
+ cache_tree_init(&tree->state);
+ cache_tree_init(&tree->cache);
+ tree->cache_size = 0;
+}
+
+static struct extent_state *alloc_extent_state(void)
+{
+ struct extent_state *state;
+
+ state = malloc(sizeof(*state));
+ if (!state)
+ return NULL;
+ state->cache_node.objectid = 0;
+ state->refs = 1;
+ state->state = 0;
+ state->xprivate = 0;
+ return state;
+}
+
+static void btrfs_free_extent_state(struct extent_state *state)
+{
+ state->refs--;
+ BUG_ON(state->refs < 0);
+ if (state->refs == 0)
+ free(state);
+}
+
+static void free_extent_state_func(struct cache_extent *cache)
+{
+ struct extent_state *es;
+
+ es = container_of(cache, struct extent_state, cache_node);
+ btrfs_free_extent_state(es);
+}
+
+static void free_extent_buffer_final(struct extent_buffer *eb);
+void extent_io_tree_cleanup(struct extent_io_tree *tree)
+{
+ cache_tree_free_extents(&tree->state, free_extent_state_func);
+}
+
+static inline void update_extent_state(struct extent_state *state)
+{
+ state->cache_node.start = state->start;
+ state->cache_node.size = state->end + 1 - state->start;
+}
+
+/*
+ * Utility function to look for merge candidates inside a given range.
+ * Any extents with matching state are merged together into a single
+ * extent in the tree. Extents with EXTENT_IO in their state field are
+ * not merged
+ */
+static int merge_state(struct extent_io_tree *tree,
+ struct extent_state *state)
+{
+ struct extent_state *other;
+ struct cache_extent *other_node;
+
+ if (state->state & EXTENT_IOBITS)
+ return 0;
+
+ other_node = prev_cache_extent(&state->cache_node);
+ if (other_node) {
+ other = container_of(other_node, struct extent_state,
+ cache_node);
+ if (other->end == state->start - 1 &&
+ other->state == state->state) {
+ state->start = other->start;
+ update_extent_state(state);
+ remove_cache_extent(&tree->state, &other->cache_node);
+ btrfs_free_extent_state(other);
+ }
+ }
+ other_node = next_cache_extent(&state->cache_node);
+ if (other_node) {
+ other = container_of(other_node, struct extent_state,
+ cache_node);
+ if (other->start == state->end + 1 &&
+ other->state == state->state) {
+ other->start = state->start;
+ update_extent_state(other);
+ remove_cache_extent(&tree->state, &state->cache_node);
+ btrfs_free_extent_state(state);
+ }
+ }
+ return 0;
+}
+
+/*
+ * insert an extent_state struct into the tree. 'bits' are set on the
+ * struct before it is inserted.
+ */
+static int insert_state(struct extent_io_tree *tree,
+ struct extent_state *state, u64 start, u64 end,
+ int bits)
+{
+ int ret;
+
+ BUG_ON(end < start);
+ state->state |= bits;
+ state->start = start;
+ state->end = end;
+ update_extent_state(state);
+ ret = insert_cache_extent(&tree->state, &state->cache_node);
+ BUG_ON(ret);
+ merge_state(tree, state);
+ return 0;
+}
+
+/*
+ * split a given extent state struct in two, inserting the preallocated
+ * struct 'prealloc' as the newly created second half. 'split' indicates an
+ * offset inside 'orig' where it should be split.
+ */
+static int split_state(struct extent_io_tree *tree, struct extent_state *orig,
+ struct extent_state *prealloc, u64 split)
+{
+ int ret;
+ prealloc->start = orig->start;
+ prealloc->end = split - 1;
+ prealloc->state = orig->state;
+ update_extent_state(prealloc);
+ orig->start = split;
+ update_extent_state(orig);
+ ret = insert_cache_extent(&tree->state, &prealloc->cache_node);
+ BUG_ON(ret);
+ return 0;
+}
+
+/*
+ * clear some bits on a range in the tree.
+ */
+static int clear_state_bit(struct extent_io_tree *tree,
+ struct extent_state *state, int bits)
+{
+ int ret = state->state & bits;
+
+ state->state &= ~bits;
+ if (state->state == 0) {
+ remove_cache_extent(&tree->state, &state->cache_node);
+ btrfs_free_extent_state(state);
+ } else {
+ merge_state(tree, state);
+ }
+ return ret;
+}
+
+/*
+ * extent_buffer_bitmap_set - set an area of a bitmap
+ * @eb: the extent buffer
+ * @start: offset of the bitmap item in the extent buffer
+ * @pos: bit number of the first bit
+ * @len: number of bits to set
+ */
+void extent_buffer_bitmap_set(struct extent_buffer *eb, unsigned long start,
+ unsigned long pos, unsigned long len)
+{
+ u8 *p = (u8 *)eb->data + start + BIT_BYTE(pos);
+ const unsigned int size = pos + len;
+ int bits_to_set = BITS_PER_BYTE - (pos % BITS_PER_BYTE);
+ u8 mask_to_set = BITMAP_FIRST_BYTE_MASK(pos);
+
+ while (len >= bits_to_set) {
+ *p |= mask_to_set;
+ len -= bits_to_set;
+ bits_to_set = BITS_PER_BYTE;
+ mask_to_set = ~0;
+ p++;
+ }
+ if (len) {
+ mask_to_set &= BITMAP_LAST_BYTE_MASK(size);
+ *p |= mask_to_set;
+ }
+}
+
+/*
+ * extent_buffer_bitmap_clear - clear an area of a bitmap
+ * @eb: the extent buffer
+ * @start: offset of the bitmap item in the extent buffer
+ * @pos: bit number of the first bit
+ * @len: number of bits to clear
+ */
+void extent_buffer_bitmap_clear(struct extent_buffer *eb, unsigned long start,
+ unsigned long pos, unsigned long len)
+{
+ u8 *p = (u8 *)eb->data + start + BIT_BYTE(pos);
+ const unsigned int size = pos + len;
+ int bits_to_clear = BITS_PER_BYTE - (pos % BITS_PER_BYTE);
+ u8 mask_to_clear = BITMAP_FIRST_BYTE_MASK(pos);
+
+ while (len >= bits_to_clear) {
+ *p &= ~mask_to_clear;
+ len -= bits_to_clear;
+ bits_to_clear = BITS_PER_BYTE;
+ mask_to_clear = ~0;
+ p++;
+ }
+ if (len) {
+ mask_to_clear &= BITMAP_LAST_BYTE_MASK(size);
+ *p &= ~mask_to_clear;
+ }
+}
+
+/*
+ * clear some bits on a range in the tree.
+ */
+int clear_extent_bits(struct extent_io_tree *tree, u64 start, u64 end, int bits)
+{
+ struct extent_state *state;
+ struct extent_state *prealloc = NULL;
+ struct cache_extent *node;
+ u64 last_end;
+ int err;
+ int set = 0;
+
+again:
+ if (!prealloc) {
+ prealloc = alloc_extent_state();
+ if (!prealloc)
+ return -ENOMEM;
+ }
+
+ /*
+ * this search will find the extents that end after
+ * our range starts
+ */
+ node = search_cache_extent(&tree->state, start);
+ if (!node)
+ goto out;
+ state = container_of(node, struct extent_state, cache_node);
+ if (state->start > end)
+ goto out;
+ last_end = state->end;
+
+ /*
+ * | ---- desired range ---- |
+ * | state | or
+ * | ------------- state -------------- |
+ *
+ * We need to split the extent we found, and may flip
+ * bits on second half.
+ *
+ * If the extent we found extends past our range, we
+ * just split and search again. It'll get split again
+ * the next time though.
+ *
+ * If the extent we found is inside our range, we clear
+ * the desired bit on it.
+ */
+ if (state->start < start) {
+ err = split_state(tree, state, prealloc, start);
+ BUG_ON(err == -EEXIST);
+ prealloc = NULL;
+ if (err)
+ goto out;
+ if (state->end <= end) {
+ set |= clear_state_bit(tree, state, bits);
+ if (last_end == (u64)-1)
+ goto out;
+ start = last_end + 1;
+ } else {
+ start = state->start;
+ }
+ goto search_again;
+ }
+ /*
+ * | ---- desired range ---- |
+ * | state |
+ * We need to split the extent, and clear the bit
+ * on the first half
+ */
+ if (state->start <= end && state->end > end) {
+ err = split_state(tree, state, prealloc, end + 1);
+ BUG_ON(err == -EEXIST);
+
+ set |= clear_state_bit(tree, prealloc, bits);
+ prealloc = NULL;
+ goto out;
+ }
+
+ start = state->end + 1;
+ set |= clear_state_bit(tree, state, bits);
+ if (last_end == (u64)-1)
+ goto out;
+ start = last_end + 1;
+ goto search_again;
+out:
+ if (prealloc)
+ btrfs_free_extent_state(prealloc);
+ return set;
+
+search_again:
+ if (start > end)
+ goto out;
+ goto again;
+}
+
+/*
+ * set some bits on a range in the tree.
+ */
+int set_extent_bits(struct extent_io_tree *tree, u64 start, u64 end, int bits)
+{
+ struct extent_state *state;
+ struct extent_state *prealloc = NULL;
+ struct cache_extent *node;
+ int err = 0;
+ u64 last_start;
+ u64 last_end;
+again:
+ if (!prealloc) {
+ prealloc = alloc_extent_state();
+ if (!prealloc)
+ return -ENOMEM;
+ }
+
+ /*
+ * this search will find the extents that end after
+ * our range starts
+ */
+ node = search_cache_extent(&tree->state, start);
+ if (!node) {
+ err = insert_state(tree, prealloc, start, end, bits);
+ BUG_ON(err == -EEXIST);
+ prealloc = NULL;
+ goto out;
+ }
+
+ state = container_of(node, struct extent_state, cache_node);
+ last_start = state->start;
+ last_end = state->end;
+
+ /*
+ * | ---- desired range ---- |
+ * | state |
+ *
+ * Just lock what we found and keep going
+ */
+ if (state->start == start && state->end <= end) {
+ state->state |= bits;
+ merge_state(tree, state);
+ if (last_end == (u64)-1)
+ goto out;
+ start = last_end + 1;
+ goto search_again;
+ }
+ /*
+ * | ---- desired range ---- |
+ * | state |
+ * or
+ * | ------------- state -------------- |
+ *
+ * We need to split the extent we found, and may flip bits on
+ * second half.
+ *
+ * If the extent we found extends past our
+ * range, we just split and search again. It'll get split
+ * again the next time though.
+ *
+ * If the extent we found is inside our range, we set the
+ * desired bit on it.
+ */
+ if (state->start < start) {
+ err = split_state(tree, state, prealloc, start);
+ BUG_ON(err == -EEXIST);
+ prealloc = NULL;
+ if (err)
+ goto out;
+ if (state->end <= end) {
+ state->state |= bits;
+ start = state->end + 1;
+ merge_state(tree, state);
+ if (last_end == (u64)-1)
+ goto out;
+ start = last_end + 1;
+ } else {
+ start = state->start;
+ }
+ goto search_again;
+ }
+ /*
+ * | ---- desired range ---- |
+ * | state | or | state |
+ *
+ * There's a hole, we need to insert something in it and
+ * ignore the extent we found.
+ */
+ if (state->start > start) {
+ u64 this_end;
+ if (end < last_start)
+ this_end = end;
+ else
+ this_end = last_start -1;
+ err = insert_state(tree, prealloc, start, this_end,
+ bits);
+ BUG_ON(err == -EEXIST);
+ prealloc = NULL;
+ if (err)
+ goto out;
+ start = this_end + 1;
+ goto search_again;
+ }
+ /*
+ * | ---- desired range ---- |
+ * | ---------- state ---------- |
+ * We need to split the extent, and set the bit
+ * on the first half
+ */
+ err = split_state(tree, state, prealloc, end + 1);
+ BUG_ON(err == -EEXIST);
+
+ state->state |= bits;
+ merge_state(tree, prealloc);
+ prealloc = NULL;
+out:
+ if (prealloc)
+ btrfs_free_extent_state(prealloc);
+ return err;
+search_again:
+ if (start > end)
+ goto out;
+ goto again;
+}
+
+int set_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end)
+{
+ return set_extent_bits(tree, start, end, EXTENT_DIRTY);
+}
+
+int clear_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end)
+{
+ return clear_extent_bits(tree, start, end, EXTENT_DIRTY);
+}
+
+int find_first_extent_bit(struct extent_io_tree *tree, u64 start,
+ u64 *start_ret, u64 *end_ret, int bits)
+{
+ struct cache_extent *node;
+ struct extent_state *state;
+ int ret = 1;
+
+ /*
+ * this search will find all the extents that end after
+ * our range starts.
+ */
+ node = search_cache_extent(&tree->state, start);
+ if (!node)
+ goto out;
+
+ while(1) {
+ state = container_of(node, struct extent_state, cache_node);
+ if (state->end >= start && (state->state & bits)) {
+ *start_ret = state->start;
+ *end_ret = state->end;
+ ret = 0;
+ break;
+ }
+ node = next_cache_extent(node);
+ if (!node)
+ break;
+ }
+out:
+ return ret;
+}
+
+int test_range_bit(struct extent_io_tree *tree, u64 start, u64 end,
+ int bits, int filled)
+{
+ struct extent_state *state = NULL;
+ struct cache_extent *node;
+ int bitset = 0;
+
+ node = search_cache_extent(&tree->state, start);
+ while (node && start <= end) {
+ state = container_of(node, struct extent_state, cache_node);
+
+ if (filled && state->start > start) {
+ bitset = 0;
+ break;
+ }
+ if (state->start > end)
+ break;
+ if (state->state & bits) {
+ bitset = 1;
+ if (!filled)
+ break;
+ } else if (filled) {
+ bitset = 0;
+ break;
+ }
+ start = state->end + 1;
+ if (start > end)
+ break;
+ node = next_cache_extent(node);
+ if (!node) {
+ if (filled)
+ bitset = 0;
+ break;
+ }
+ }
+ return bitset;
+}
+
+int set_state_private(struct extent_io_tree *tree, u64 start, u64 private)
+{
+ struct cache_extent *node;
+ struct extent_state *state;
+ int ret = 0;
+
+ node = search_cache_extent(&tree->state, start);
+ if (!node) {
+ ret = -ENOENT;
+ goto out;
+ }
+ state = container_of(node, struct extent_state, cache_node);
+ if (state->start != start) {
+ ret = -ENOENT;
+ goto out;
+ }
+ state->xprivate = private;
+out:
+ return ret;
+}
+
+int get_state_private(struct extent_io_tree *tree, u64 start, u64 *private)
+{
+ struct cache_extent *node;
+ struct extent_state *state;
+ int ret = 0;
+
+ node = search_cache_extent(&tree->state, start);
+ if (!node) {
+ ret = -ENOENT;
+ goto out;
+ }
+ state = container_of(node, struct extent_state, cache_node);
+ if (state->start != start) {
+ ret = -ENOENT;
+ goto out;
+ }
+ *private = state->xprivate;
+out:
+ return ret;
+}
+
+static struct extent_buffer *__alloc_extent_buffer(struct btrfs_fs_info *info,
+ u64 bytenr, u32 blocksize)
+{
+ struct extent_buffer *eb;
+
+ eb = calloc(1, sizeof(struct extent_buffer));
+ if (!eb)
+ return NULL;
+ eb->data = malloc_cache_aligned(blocksize);
+ if (!eb->data) {
+ free(eb);
+ return NULL;
+ }
+
+ eb->start = bytenr;
+ eb->len = blocksize;
+ eb->refs = 1;
+ eb->flags = 0;
+ eb->cache_node.start = bytenr;
+ eb->cache_node.size = blocksize;
+ eb->fs_info = info;
+ memset_extent_buffer(eb, 0, 0, blocksize);
+
+ return eb;
+}
+
+struct extent_buffer *btrfs_clone_extent_buffer(struct extent_buffer *src)
+{
+ struct extent_buffer *new;
+
+ new = __alloc_extent_buffer(src->fs_info, src->start, src->len);
+ if (!new)
+ return NULL;
+
+ copy_extent_buffer(new, src, 0, 0, src->len);
+ new->flags |= EXTENT_BUFFER_DUMMY;
+
+ return new;
+}
+
+static void free_extent_buffer_final(struct extent_buffer *eb)
+{
+ BUG_ON(eb->refs);
+ if (!(eb->flags & EXTENT_BUFFER_DUMMY)) {
+ struct extent_io_tree *tree = &eb->fs_info->extent_cache;
+
+ remove_cache_extent(&tree->cache, &eb->cache_node);
+ BUG_ON(tree->cache_size < eb->len);
+ tree->cache_size -= eb->len;
+ }
+ free(eb->data);
+ free(eb);
+}
+
+static void free_extent_buffer_internal(struct extent_buffer *eb, bool free_now)
+{
+ if (!eb || IS_ERR(eb))
+ return;
+
+ eb->refs--;
+ BUG_ON(eb->refs < 0);
+ if (eb->refs == 0) {
+ if (eb->flags & EXTENT_DIRTY) {
+ error(
+ "dirty eb leak (aborted trans): start %llu len %u",
+ eb->start, eb->len);
+ }
+ if (eb->flags & EXTENT_BUFFER_DUMMY || free_now)
+ free_extent_buffer_final(eb);
+ }
+}
+
+void free_extent_buffer(struct extent_buffer *eb)
+{
+ free_extent_buffer_internal(eb, 1);
+}
+
+struct extent_buffer *find_extent_buffer(struct extent_io_tree *tree,
+ u64 bytenr, u32 blocksize)
+{
+ struct extent_buffer *eb = NULL;
+ struct cache_extent *cache;
+
+ cache = lookup_cache_extent(&tree->cache, bytenr, blocksize);
+ if (cache && cache->start == bytenr &&
+ cache->size == blocksize) {
+ eb = container_of(cache, struct extent_buffer, cache_node);
+ eb->refs++;
+ }
+ return eb;
+}
+
+struct extent_buffer *find_first_extent_buffer(struct extent_io_tree *tree,
+ u64 start)
+{
+ struct extent_buffer *eb = NULL;
+ struct cache_extent *cache;
+
+ cache = search_cache_extent(&tree->cache, start);
+ if (cache) {
+ eb = container_of(cache, struct extent_buffer, cache_node);
+ eb->refs++;
+ }
+ return eb;
+}
+
+struct extent_buffer *alloc_extent_buffer(struct btrfs_fs_info *fs_info,
+ u64 bytenr, u32 blocksize)
+{
+ struct extent_buffer *eb;
+ struct extent_io_tree *tree = &fs_info->extent_cache;
+ struct cache_extent *cache;
+
+ cache = lookup_cache_extent(&tree->cache, bytenr, blocksize);
+ if (cache && cache->start == bytenr &&
+ cache->size == blocksize) {
+ eb = container_of(cache, struct extent_buffer, cache_node);
+ eb->refs++;
+ } else {
+ int ret;
+
+ if (cache) {
+ eb = container_of(cache, struct extent_buffer,
+ cache_node);
+ free_extent_buffer(eb);
+ }
+ eb = __alloc_extent_buffer(fs_info, bytenr, blocksize);
+ if (!eb)
+ return NULL;
+ ret = insert_cache_extent(&tree->cache, &eb->cache_node);
+ if (ret) {
+ free(eb);
+ return NULL;
+ }
+ tree->cache_size += blocksize;
+ }
+ return eb;
+}
+
+/*
+ * Allocate a dummy extent buffer which won't be inserted into extent buffer
+ * cache.
+ *
+ * This mostly allows super block read write using existing eb infrastructure
+ * without pulluting the eb cache.
+ *
+ * This is especially important to avoid injecting eb->start == SZ_64K, as
+ * fuzzed image could have invalid tree bytenr covers super block range,
+ * and cause ref count underflow.
+ */
+struct extent_buffer *alloc_dummy_extent_buffer(struct btrfs_fs_info *fs_info,
+ u64 bytenr, u32 blocksize)
+{
+ struct extent_buffer *ret;
+
+ ret = __alloc_extent_buffer(fs_info, bytenr, blocksize);
+ if (!ret)
+ return NULL;
+
+ ret->flags |= EXTENT_BUFFER_DUMMY;
+
+ return ret;
+}
+
+int read_extent_from_disk(struct blk_desc *desc, struct disk_partition *part,
+ u64 physical, struct extent_buffer *eb,
+ unsigned long offset, unsigned long len)
+{
+ int ret;
+
+ ret = __btrfs_devread(desc, part, eb->data + offset, len, physical);
+ if (ret < 0)
+ goto out;
+ if (ret != len) {
+ ret = -EIO;
+ goto out;
+ }
+ ret = 0;
+out:
+ return ret;
+}
+
+int memcmp_extent_buffer(const struct extent_buffer *eb, const void *ptrv,
+ unsigned long start, unsigned long len)
+{
+ return memcmp(eb->data + start, ptrv, len);
+}
+
+void read_extent_buffer(const struct extent_buffer *eb, void *dst,
+ unsigned long start, unsigned long len)
+{
+ memcpy(dst, eb->data + start, len);
+}
+
+void write_extent_buffer(struct extent_buffer *eb, const void *src,
+ unsigned long start, unsigned long len)
+{
+ memcpy(eb->data + start, src, len);
+}
+
+void copy_extent_buffer(struct extent_buffer *dst, struct extent_buffer *src,
+ unsigned long dst_offset, unsigned long src_offset,
+ unsigned long len)
+{
+ memcpy(dst->data + dst_offset, src->data + src_offset, len);
+}
+
+void memmove_extent_buffer(struct extent_buffer *dst, unsigned long dst_offset,
+ unsigned long src_offset, unsigned long len)
+{
+ memmove(dst->data + dst_offset, dst->data + src_offset, len);
+}
+
+void memset_extent_buffer(struct extent_buffer *eb, char c,
+ unsigned long start, unsigned long len)
+{
+ memset(eb->data + start, c, len);
+}
+
+int extent_buffer_test_bit(struct extent_buffer *eb, unsigned long start,
+ unsigned long nr)
+{
+ return le_test_bit(nr, (u8 *)eb->data + start);
+}
+
+int set_extent_buffer_dirty(struct extent_buffer *eb)
+{
+ struct extent_io_tree *tree = &eb->fs_info->extent_cache;
+ if (!(eb->flags & EXTENT_DIRTY)) {
+ eb->flags |= EXTENT_DIRTY;
+ set_extent_dirty(tree, eb->start, eb->start + eb->len - 1);
+ extent_buffer_get(eb);
+ }
+ return 0;
+}
+
+int clear_extent_buffer_dirty(struct extent_buffer *eb)
+{
+ struct extent_io_tree *tree = &eb->fs_info->extent_cache;
+ if (eb->flags & EXTENT_DIRTY) {
+ eb->flags &= ~EXTENT_DIRTY;
+ clear_extent_dirty(tree, eb->start, eb->start + eb->len - 1);
+ free_extent_buffer(eb);
+ }
+ return 0;
+}
diff --git a/roms/u-boot/fs/btrfs/extent-io.h b/roms/u-boot/fs/btrfs/extent-io.h
new file mode 100644
index 000000000..6b0c87da9
--- /dev/null
+++ b/roms/u-boot/fs/btrfs/extent-io.h
@@ -0,0 +1,164 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/*
+ * Crossported from btrfs-progs/extent_io.h
+ *
+ * Modification includes:
+ * - extent_buffer:data
+ * Use pointer to provide better alignment.
+ * - Remove max_cache_size related interfaces
+ * Includes free_extent_buffer_nocache()
+ * As we don't cache eb in U-boot.
+ * - Include headers
+ *
+ * Write related functions are kept as we still need to modify dummy extent
+ * buffers even in RO environment.
+ */
+#ifndef __BTRFS_EXTENT_IO_H__
+#define __BTRFS_EXTENT_IO_H__
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/err.h>
+#include <linux/bitops.h>
+#include <fs_internal.h>
+#include "extent-cache.h"
+
+#define EXTENT_DIRTY (1U << 0)
+#define EXTENT_WRITEBACK (1U << 1)
+#define EXTENT_UPTODATE (1U << 2)
+#define EXTENT_LOCKED (1U << 3)
+#define EXTENT_NEW (1U << 4)
+#define EXTENT_DELALLOC (1U << 5)
+#define EXTENT_DEFRAG (1U << 6)
+#define EXTENT_DEFRAG_DONE (1U << 7)
+#define EXTENT_BUFFER_FILLED (1U << 8)
+#define EXTENT_CSUM (1U << 9)
+#define EXTENT_BAD_TRANSID (1U << 10)
+#define EXTENT_BUFFER_DUMMY (1U << 11)
+#define EXTENT_IOBITS (EXTENT_LOCKED | EXTENT_WRITEBACK)
+
+#define BLOCK_GROUP_DATA (1U << 1)
+#define BLOCK_GROUP_METADATA (1U << 2)
+#define BLOCK_GROUP_SYSTEM (1U << 4)
+
+/*
+ * The extent buffer bitmap operations are done with byte granularity instead of
+ * word granularity for two reasons:
+ * 1. The bitmaps must be little-endian on disk.
+ * 2. Bitmap items are not guaranteed to be aligned to a word and therefore a
+ * single word in a bitmap may straddle two pages in the extent buffer.
+ */
+#define BIT_BYTE(nr) ((nr) / BITS_PER_BYTE)
+#define BYTE_MASK ((1 << BITS_PER_BYTE) - 1)
+#define BITMAP_FIRST_BYTE_MASK(start) \
+ ((BYTE_MASK << ((start) & (BITS_PER_BYTE - 1))) & BYTE_MASK)
+#define BITMAP_LAST_BYTE_MASK(nbits) \
+ (BYTE_MASK >> (-(nbits) & (BITS_PER_BYTE - 1)))
+
+static inline int le_test_bit(int nr, const u8 *addr)
+{
+ return 1U & (addr[BIT_BYTE(nr)] >> (nr & (BITS_PER_BYTE-1)));
+}
+
+struct btrfs_fs_info;
+
+struct extent_io_tree {
+ struct cache_tree state;
+ struct cache_tree cache;
+ u64 cache_size;
+};
+
+struct extent_state {
+ struct cache_extent cache_node;
+ u64 start;
+ u64 end;
+ int refs;
+ unsigned long state;
+ u64 xprivate;
+};
+
+struct extent_buffer {
+ struct cache_extent cache_node;
+ u64 start;
+ u32 len;
+ int refs;
+ u32 flags;
+ struct btrfs_fs_info *fs_info;
+ char *data;
+};
+
+static inline void extent_buffer_get(struct extent_buffer *eb)
+{
+ eb->refs++;
+}
+
+void extent_io_tree_init(struct extent_io_tree *tree);
+void extent_io_tree_cleanup(struct extent_io_tree *tree);
+int set_extent_bits(struct extent_io_tree *tree, u64 start, u64 end, int bits);
+int clear_extent_bits(struct extent_io_tree *tree, u64 start, u64 end, int bits);
+int find_first_extent_bit(struct extent_io_tree *tree, u64 start,
+ u64 *start_ret, u64 *end_ret, int bits);
+int test_range_bit(struct extent_io_tree *tree, u64 start, u64 end,
+ int bits, int filled);
+int set_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end);
+int clear_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end);
+static inline int set_extent_buffer_uptodate(struct extent_buffer *eb)
+{
+ eb->flags |= EXTENT_UPTODATE;
+ return 0;
+}
+
+static inline int clear_extent_buffer_uptodate(struct extent_buffer *eb)
+{
+ eb->flags &= ~EXTENT_UPTODATE;
+ return 0;
+}
+
+static inline int extent_buffer_uptodate(struct extent_buffer *eb)
+{
+ if (!eb || IS_ERR(eb))
+ return 0;
+ if (eb->flags & EXTENT_UPTODATE)
+ return 1;
+ return 0;
+}
+
+int set_state_private(struct extent_io_tree *tree, u64 start, u64 xprivate);
+int get_state_private(struct extent_io_tree *tree, u64 start, u64 *xprivate);
+struct extent_buffer *find_extent_buffer(struct extent_io_tree *tree,
+ u64 bytenr, u32 blocksize);
+struct extent_buffer *find_first_extent_buffer(struct extent_io_tree *tree,
+ u64 start);
+struct extent_buffer *alloc_extent_buffer(struct btrfs_fs_info *fs_info,
+ u64 bytenr, u32 blocksize);
+struct extent_buffer *btrfs_clone_extent_buffer(struct extent_buffer *src);
+struct extent_buffer *alloc_dummy_extent_buffer(struct btrfs_fs_info *fs_info,
+ u64 bytenr, u32 blocksize);
+void free_extent_buffer(struct extent_buffer *eb);
+int read_extent_from_disk(struct blk_desc *desc, struct disk_partition *part,
+ u64 physical, struct extent_buffer *eb,
+ unsigned long offset, unsigned long len);
+int memcmp_extent_buffer(const struct extent_buffer *eb, const void *ptrv,
+ unsigned long start, unsigned long len);
+void read_extent_buffer(const struct extent_buffer *eb, void *dst,
+ unsigned long start, unsigned long len);
+void write_extent_buffer(struct extent_buffer *eb, const void *src,
+ unsigned long start, unsigned long len);
+void copy_extent_buffer(struct extent_buffer *dst, struct extent_buffer *src,
+ unsigned long dst_offset, unsigned long src_offset,
+ unsigned long len);
+void memmove_extent_buffer(struct extent_buffer *dst, unsigned long dst_offset,
+ unsigned long src_offset, unsigned long len);
+void memset_extent_buffer(struct extent_buffer *eb, char c,
+ unsigned long start, unsigned long len);
+int extent_buffer_test_bit(struct extent_buffer *eb, unsigned long start,
+ unsigned long nr);
+int set_extent_buffer_dirty(struct extent_buffer *eb);
+int clear_extent_buffer_dirty(struct extent_buffer *eb);
+void extent_buffer_bitmap_clear(struct extent_buffer *eb, unsigned long start,
+ unsigned long pos, unsigned long len);
+void extent_buffer_bitmap_set(struct extent_buffer *eb, unsigned long start,
+ unsigned long pos, unsigned long len);
+
+#endif
diff --git a/roms/u-boot/fs/btrfs/inode.c b/roms/u-boot/fs/btrfs/inode.c
new file mode 100644
index 000000000..2c2379303
--- /dev/null
+++ b/roms/u-boot/fs/btrfs/inode.c
@@ -0,0 +1,767 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * BTRFS filesystem implementation for U-Boot
+ *
+ * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz
+ */
+
+#include <linux/kernel.h>
+#include <malloc.h>
+#include <memalign.h>
+#include "btrfs.h"
+#include "disk-io.h"
+#include "volumes.h"
+
+/*
+ * Read the content of symlink inode @ino of @root, into @target.
+ * NOTE: @target will not be \0 termiated, caller should handle it properly.
+ *
+ * Return the number of read data.
+ * Return <0 for error.
+ */
+int btrfs_readlink(struct btrfs_root *root, u64 ino, char *target)
+{
+ struct btrfs_path path;
+ struct btrfs_key key;
+ struct btrfs_file_extent_item *fi;
+ int ret;
+
+ key.objectid = ino;
+ key.type = BTRFS_EXTENT_DATA_KEY;
+ key.offset = 0;
+ btrfs_init_path(&path);
+
+ ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
+ if (ret < 0)
+ return ret;
+ if (ret > 0) {
+ ret = -ENOENT;
+ goto out;
+ }
+ fi = btrfs_item_ptr(path.nodes[0], path.slots[0],
+ struct btrfs_file_extent_item);
+ if (btrfs_file_extent_type(path.nodes[0], fi) !=
+ BTRFS_FILE_EXTENT_INLINE) {
+ ret = -EUCLEAN;
+ error("Extent for symlink %llu must be INLINE type!", ino);
+ goto out;
+ }
+ if (btrfs_file_extent_compression(path.nodes[0], fi) !=
+ BTRFS_COMPRESS_NONE) {
+ ret = -EUCLEAN;
+ error("Extent for symlink %llu must not be compressed!", ino);
+ goto out;
+ }
+ if (btrfs_file_extent_ram_bytes(path.nodes[0], fi) >=
+ root->fs_info->sectorsize) {
+ ret = -EUCLEAN;
+ error("Symlink %llu extent data too large (%llu)!\n",
+ ino, btrfs_file_extent_ram_bytes(path.nodes[0], fi));
+ goto out;
+ }
+ read_extent_buffer(path.nodes[0], target,
+ btrfs_file_extent_inline_start(fi),
+ btrfs_file_extent_ram_bytes(path.nodes[0], fi));
+ ret = btrfs_file_extent_ram_bytes(path.nodes[0], fi);
+out:
+ btrfs_release_path(&path);
+ return ret;
+}
+
+static int lookup_root_ref(struct btrfs_fs_info *fs_info,
+ u64 rootid, u64 *root_ret, u64 *dir_ret)
+{
+ struct btrfs_root *root = fs_info->tree_root;
+ struct btrfs_root_ref *root_ref;
+ struct btrfs_path path;
+ struct btrfs_key key;
+ int ret;
+
+ btrfs_init_path(&path);
+ key.objectid = rootid;
+ key.type = BTRFS_ROOT_BACKREF_KEY;
+ key.offset = (u64)-1;
+
+ ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
+ if (ret < 0)
+ return ret;
+ /* Should not happen */
+ if (ret == 0) {
+ ret = -EUCLEAN;
+ goto out;
+ }
+ ret = btrfs_previous_item(root, &path, rootid, BTRFS_ROOT_BACKREF_KEY);
+ if (ret < 0)
+ goto out;
+ if (ret > 0) {
+ ret = -ENOENT;
+ goto out;
+ }
+ btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
+ root_ref = btrfs_item_ptr(path.nodes[0], path.slots[0],
+ struct btrfs_root_ref);
+ *root_ret = key.offset;
+ *dir_ret = btrfs_root_ref_dirid(path.nodes[0], root_ref);
+out:
+ btrfs_release_path(&path);
+ return ret;
+}
+
+/*
+ * To get the parent inode of @ino of @root.
+ *
+ * @root_ret and @ino_ret will be filled.
+ *
+ * NOTE: This function is not reliable. It can only get one parent inode.
+ * The get the proper parent inode, we need a full VFS inodes stack to
+ * resolve properly.
+ */
+static int get_parent_inode(struct btrfs_root *root, u64 ino,
+ struct btrfs_root **root_ret, u64 *ino_ret)
+{
+ struct btrfs_fs_info *fs_info = root->fs_info;
+ struct btrfs_path path;
+ struct btrfs_key key;
+ int ret;
+
+ if (ino == BTRFS_FIRST_FREE_OBJECTID) {
+ u64 parent_root = -1;
+
+ /* It's top level already, no more parent */
+ if (root->root_key.objectid == BTRFS_FS_TREE_OBJECTID) {
+ *root_ret = fs_info->fs_root;
+ *ino_ret = BTRFS_FIRST_FREE_OBJECTID;
+ return 0;
+ }
+
+ ret = lookup_root_ref(fs_info, root->root_key.objectid,
+ &parent_root, ino_ret);
+ if (ret < 0)
+ return ret;
+
+ key.objectid = parent_root;
+ key.type = BTRFS_ROOT_ITEM_KEY;
+ key.offset = (u64)-1;
+ *root_ret = btrfs_read_fs_root(fs_info, &key);
+ if (IS_ERR(*root_ret))
+ return PTR_ERR(*root_ret);
+
+ return 0;
+ }
+
+ btrfs_init_path(&path);
+ key.objectid = ino;
+ key.type = BTRFS_INODE_REF_KEY;
+ key.offset = (u64)-1;
+
+ ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
+ if (ret < 0)
+ return ret;
+ /* Should not happen */
+ if (ret == 0) {
+ ret = -EUCLEAN;
+ goto out;
+ }
+ ret = btrfs_previous_item(root, &path, ino, BTRFS_INODE_REF_KEY);
+ if (ret < 0)
+ goto out;
+ if (ret > 0) {
+ ret = -ENOENT;
+ goto out;
+ }
+ btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
+ *root_ret = root;
+ *ino_ret = key.offset;
+out:
+ btrfs_release_path(&path);
+ return ret;
+}
+
+static inline int next_length(const char *path)
+{
+ int res = 0;
+ while (*path != '\0' && *path != '/') {
+ ++res;
+ ++path;
+ if (res > BTRFS_NAME_LEN)
+ break;
+ }
+ return res;
+}
+
+static inline const char *skip_current_directories(const char *cur)
+{
+ while (1) {
+ if (cur[0] == '/')
+ ++cur;
+ else if (cur[0] == '.' && cur[1] == '/')
+ cur += 2;
+ else
+ break;
+ }
+
+ return cur;
+}
+
+/*
+ * Resolve one filename of @ino of @root.
+ *
+ * key_ret: The child key (either INODE_ITEM or ROOT_ITEM type)
+ * type_ret: BTRFS_FT_* of the child inode.
+ *
+ * Return 0 with above members filled.
+ * Return <0 for error.
+ */
+static int resolve_one_filename(struct btrfs_root *root, u64 ino,
+ const char *name, int namelen,
+ struct btrfs_key *key_ret, u8 *type_ret)
+{
+ struct btrfs_dir_item *dir_item;
+ struct btrfs_path path;
+ int ret = 0;
+
+ btrfs_init_path(&path);
+
+ dir_item = btrfs_lookup_dir_item(NULL, root, &path, ino, name,
+ namelen, 0);
+ if (IS_ERR(dir_item)) {
+ ret = PTR_ERR(dir_item);
+ goto out;
+ }
+
+ btrfs_dir_item_key_to_cpu(path.nodes[0], dir_item, key_ret);
+ *type_ret = btrfs_dir_type(path.nodes[0], dir_item);
+out:
+ btrfs_release_path(&path);
+ return ret;
+}
+
+/*
+ * Resolve a full path @filename. The start point is @ino of @root.
+ *
+ * The result will be filled into @root_ret, @ino_ret and @type_ret.
+ */
+int btrfs_lookup_path(struct btrfs_root *root, u64 ino, const char *filename,
+ struct btrfs_root **root_ret, u64 *ino_ret,
+ u8 *type_ret, int symlink_limit)
+{
+ struct btrfs_fs_info *fs_info = root->fs_info;
+ struct btrfs_root *next_root;
+ struct btrfs_key key;
+ const char *cur = filename;
+ u64 next_ino;
+ u8 next_type;
+ u8 type = BTRFS_FT_UNKNOWN;
+ int len;
+ int ret = 0;
+
+ /* If the path is absolute path, also search from fs root */
+ if (*cur == '/') {
+ root = fs_info->fs_root;
+ ino = btrfs_root_dirid(&root->root_item);
+ type = BTRFS_FT_DIR;
+ }
+
+ while (*cur != '\0') {
+ cur = skip_current_directories(cur);
+
+ len = next_length(cur);
+ if (len > BTRFS_NAME_LEN) {
+ error("%s: Name too long at \"%.*s\"", __func__,
+ BTRFS_NAME_LEN, cur);
+ return -ENAMETOOLONG;
+ }
+
+ if (len == 1 && cur[0] == '.')
+ break;
+
+ if (len == 2 && cur[0] == '.' && cur[1] == '.') {
+ /* Go one level up */
+ ret = get_parent_inode(root, ino, &next_root, &next_ino);
+ if (ret < 0)
+ return ret;
+ root = next_root;
+ ino = next_ino;
+ goto next;
+ }
+
+ if (!*cur)
+ break;
+
+ ret = resolve_one_filename(root, ino, cur, len, &key, &type);
+ if (ret < 0)
+ return ret;
+
+ if (key.type == BTRFS_ROOT_ITEM_KEY) {
+ /* Child inode is a subvolume */
+
+ next_root = btrfs_read_fs_root(fs_info, &key);
+ if (IS_ERR(next_root))
+ return PTR_ERR(next_root);
+ root = next_root;
+ ino = btrfs_root_dirid(&root->root_item);
+ } else if (type == BTRFS_FT_SYMLINK && symlink_limit >= 0) {
+ /* Child inode is a symlink */
+
+ char *target;
+
+ if (symlink_limit == 0) {
+ error("%s: Too much symlinks!", __func__);
+ return -EMLINK;
+ }
+ target = malloc(fs_info->sectorsize);
+ if (!target)
+ return -ENOMEM;
+ ret = btrfs_readlink(root, key.objectid, target);
+ if (ret < 0) {
+ free(target);
+ return ret;
+ }
+ target[ret] = '\0';
+
+ ret = btrfs_lookup_path(root, ino, target, &next_root,
+ &next_ino, &next_type,
+ symlink_limit);
+ if (ret < 0)
+ return ret;
+ root = next_root;
+ ino = next_ino;
+ type = next_type;
+ } else {
+ /* Child inode is an inode */
+ ino = key.objectid;
+ }
+next:
+ cur += len;
+ }
+
+ /* We haven't found anything, but still get no error? */
+ if (type == BTRFS_FT_UNKNOWN && !ret)
+ ret = -EUCLEAN;
+
+ if (!ret) {
+ *root_ret = root;
+ *ino_ret = ino;
+ *type_ret = type;
+ }
+
+ return ret;
+}
+
+/*
+ * Read out inline extent.
+ *
+ * Since inline extent should only exist for offset 0, no need for extra
+ * parameters.
+ * Truncating should be handled by the caller.
+ *
+ * Return the number of bytes read.
+ * Return <0 for error.
+ */
+int btrfs_read_extent_inline(struct btrfs_path *path,
+ struct btrfs_file_extent_item *fi, char *dest)
+{
+ struct extent_buffer *leaf = path->nodes[0];
+ int slot = path->slots[0];
+ char *cbuf = NULL;
+ char *dbuf = NULL;
+ u32 csize;
+ u32 dsize;
+ int ret;
+
+ csize = btrfs_file_extent_inline_item_len(leaf, btrfs_item_nr(slot));
+ if (btrfs_file_extent_compression(leaf, fi) == BTRFS_COMPRESS_NONE) {
+ /* Uncompressed, just read it out */
+ read_extent_buffer(leaf, dest,
+ btrfs_file_extent_inline_start(fi),
+ csize);
+ return csize;
+ }
+
+ /* Compressed extent, prepare the compressed and data buffer */
+ dsize = btrfs_file_extent_ram_bytes(leaf, fi);
+ cbuf = malloc(csize);
+ dbuf = malloc(dsize);
+ if (!cbuf || !dbuf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ read_extent_buffer(leaf, cbuf, btrfs_file_extent_inline_start(fi),
+ csize);
+ ret = btrfs_decompress(btrfs_file_extent_compression(leaf, fi),
+ cbuf, csize, dbuf, dsize);
+ if (ret == (u32)-1) {
+ ret = -EIO;
+ goto out;
+ }
+ /*
+ * The compressed part ends before sector boundary, the remaining needs
+ * to be zeroed out.
+ */
+ if (ret < dsize)
+ memset(dbuf + ret, 0, dsize - ret);
+ memcpy(dest, dbuf, dsize);
+ ret = dsize;
+out:
+ free(cbuf);
+ free(dbuf);
+ return ret;
+}
+
+/*
+ * Read out regular extent.
+ *
+ * Truncating should be handled by the caller.
+ *
+ * @offset and @len should not cross the extent boundary.
+ * Return the number of bytes read.
+ * Return <0 for error.
+ */
+int btrfs_read_extent_reg(struct btrfs_path *path,
+ struct btrfs_file_extent_item *fi, u64 offset,
+ int len, char *dest)
+{
+ struct extent_buffer *leaf = path->nodes[0];
+ struct btrfs_fs_info *fs_info = leaf->fs_info;
+ struct btrfs_key key;
+ u64 extent_num_bytes;
+ u64 disk_bytenr;
+ u64 read;
+ char *cbuf = NULL;
+ char *dbuf = NULL;
+ u32 csize;
+ u32 dsize;
+ bool finished = false;
+ int num_copies;
+ int i;
+ int slot = path->slots[0];
+ int ret;
+
+ btrfs_item_key_to_cpu(leaf, &key, slot);
+ extent_num_bytes = btrfs_file_extent_num_bytes(leaf, fi);
+ ASSERT(IS_ALIGNED(offset, fs_info->sectorsize) &&
+ IS_ALIGNED(len, fs_info->sectorsize));
+ ASSERT(offset >= key.offset &&
+ offset + len <= key.offset + extent_num_bytes);
+
+ /* Preallocated or hole , fill @dest with zero */
+ if (btrfs_file_extent_type(leaf, fi) == BTRFS_FILE_EXTENT_PREALLOC ||
+ btrfs_file_extent_disk_bytenr(leaf, fi) == 0) {
+ memset(dest, 0, len);
+ return len;
+ }
+
+ if (btrfs_file_extent_compression(leaf, fi) == BTRFS_COMPRESS_NONE) {
+ u64 logical;
+
+ logical = btrfs_file_extent_disk_bytenr(leaf, fi) +
+ btrfs_file_extent_offset(leaf, fi) +
+ offset - key.offset;
+ read = len;
+
+ num_copies = btrfs_num_copies(fs_info, logical, len);
+ for (i = 1; i <= num_copies; i++) {
+ ret = read_extent_data(fs_info, dest, logical, &read, i);
+ if (ret < 0 || read != len)
+ continue;
+ finished = true;
+ break;
+ }
+ if (!finished)
+ return -EIO;
+ return len;
+ }
+
+ csize = btrfs_file_extent_disk_num_bytes(leaf, fi);
+ dsize = btrfs_file_extent_ram_bytes(leaf, fi);
+ disk_bytenr = btrfs_file_extent_disk_bytenr(leaf, fi);
+ num_copies = btrfs_num_copies(fs_info, disk_bytenr, csize);
+
+ cbuf = malloc_cache_aligned(csize);
+ dbuf = malloc_cache_aligned(dsize);
+ if (!cbuf || !dbuf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ /* For compressed extent, we must read the whole on-disk extent */
+ for (i = 1; i <= num_copies; i++) {
+ read = csize;
+ ret = read_extent_data(fs_info, cbuf, disk_bytenr,
+ &read, i);
+ if (ret < 0 || read != csize)
+ continue;
+ finished = true;
+ break;
+ }
+ if (!finished) {
+ ret = -EIO;
+ goto out;
+ }
+
+ ret = btrfs_decompress(btrfs_file_extent_compression(leaf, fi), cbuf,
+ csize, dbuf, dsize);
+ if (ret == (u32)-1) {
+ ret = -EIO;
+ goto out;
+ }
+ /*
+ * The compressed part ends before sector boundary, the remaining needs
+ * to be zeroed out.
+ */
+ if (ret < dsize)
+ memset(dbuf + ret, 0, dsize - ret);
+ /* Then copy the needed part */
+ memcpy(dest, dbuf + btrfs_file_extent_offset(leaf, fi), len);
+ ret = len;
+out:
+ free(cbuf);
+ free(dbuf);
+ return ret;
+}
+
+/*
+ * Get the first file extent that covers bytenr @file_offset.
+ *
+ * @file_offset must be aligned to sectorsize.
+ *
+ * return 0 for found, and path points to the file extent.
+ * return >0 for not found, and fill @next_offset.
+ * @next_offset can be 0 if there is no next file extent.
+ * return <0 for error.
+ */
+static int lookup_data_extent(struct btrfs_root *root, struct btrfs_path *path,
+ u64 ino, u64 file_offset, u64 *next_offset)
+{
+ struct btrfs_key key;
+ struct btrfs_file_extent_item *fi;
+ u8 extent_type;
+ int ret = 0;
+
+ ASSERT(IS_ALIGNED(file_offset, root->fs_info->sectorsize));
+ key.objectid = ino;
+ key.type = BTRFS_EXTENT_DATA_KEY;
+ key.offset = file_offset;
+
+ ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+ /* Error or we're already at the file extent */
+ if (ret <= 0)
+ return ret;
+ if (ret > 0) {
+ /* Check previous file extent */
+ ret = btrfs_previous_item(root, path, ino,
+ BTRFS_EXTENT_DATA_KEY);
+ if (ret < 0)
+ return ret;
+ if (ret > 0)
+ goto check_next;
+ }
+ /* Now the key.offset must be smaller than @file_offset */
+ btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
+ if (key.objectid != ino ||
+ key.type != BTRFS_EXTENT_DATA_KEY)
+ goto check_next;
+
+ fi = btrfs_item_ptr(path->nodes[0], path->slots[0],
+ struct btrfs_file_extent_item);
+ extent_type = btrfs_file_extent_type(path->nodes[0], fi);
+ if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
+ if (file_offset == 0)
+ return 0;
+ /* Inline extent should be the only extent, no next extent. */
+ *next_offset = 0;
+ return 1;
+ }
+
+ /* This file extent covers @file_offset */
+ if (key.offset <= file_offset && key.offset +
+ btrfs_file_extent_num_bytes(path->nodes[0], fi) > file_offset)
+ return 0;
+check_next:
+ ret = btrfs_next_item(root, path);
+ if (ret < 0)
+ return ret;
+ if (ret > 0) {
+ *next_offset = 0;
+ return 1;
+ }
+
+ btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
+ fi = btrfs_item_ptr(path->nodes[0], path->slots[0],
+ struct btrfs_file_extent_item);
+ /* Next next data extent */
+ if (key.objectid != ino ||
+ key.type != BTRFS_EXTENT_DATA_KEY) {
+ *next_offset = 0;
+ return 1;
+ }
+ /* Current file extent already beyond @file_offset */
+ if (key.offset > file_offset) {
+ *next_offset = key.offset;
+ return 1;
+ }
+ /* This file extent covers @file_offset */
+ if (key.offset <= file_offset && key.offset +
+ btrfs_file_extent_num_bytes(path->nodes[0], fi) > file_offset)
+ return 0;
+ /* This file extent ends before @file_offset, check next */
+ ret = btrfs_next_item(root, path);
+ if (ret < 0)
+ return ret;
+ if (ret > 0) {
+ *next_offset = 0;
+ return 1;
+ }
+ btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
+ if (key.type != BTRFS_EXTENT_DATA_KEY || key.objectid != ino) {
+ *next_offset = 0;
+ return 1;
+ }
+ *next_offset = key.offset;
+ return 1;
+}
+
+static int read_and_truncate_page(struct btrfs_path *path,
+ struct btrfs_file_extent_item *fi,
+ int start, int len, char *dest)
+{
+ struct extent_buffer *leaf = path->nodes[0];
+ struct btrfs_fs_info *fs_info = leaf->fs_info;
+ u64 aligned_start = round_down(start, fs_info->sectorsize);
+ u8 extent_type;
+ char *buf;
+ int page_off = start - aligned_start;
+ int page_len = fs_info->sectorsize - page_off;
+ int ret;
+
+ ASSERT(start + len <= aligned_start + fs_info->sectorsize);
+ buf = malloc_cache_aligned(fs_info->sectorsize);
+ if (!buf)
+ return -ENOMEM;
+
+ extent_type = btrfs_file_extent_type(leaf, fi);
+ if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
+ ret = btrfs_read_extent_inline(path, fi, buf);
+ memcpy(dest, buf + page_off, min(page_len, ret));
+ free(buf);
+ return len;
+ }
+
+ ret = btrfs_read_extent_reg(path, fi,
+ round_down(start, fs_info->sectorsize),
+ fs_info->sectorsize, buf);
+ if (ret < 0) {
+ free(buf);
+ return ret;
+ }
+ memcpy(dest, buf + page_off, page_len);
+ free(buf);
+ return len;
+}
+
+int btrfs_file_read(struct btrfs_root *root, u64 ino, u64 file_offset, u64 len,
+ char *dest)
+{
+ struct btrfs_fs_info *fs_info = root->fs_info;
+ struct btrfs_file_extent_item *fi;
+ struct btrfs_path path;
+ struct btrfs_key key;
+ u64 aligned_start = round_down(file_offset, fs_info->sectorsize);
+ u64 aligned_end = round_down(file_offset + len, fs_info->sectorsize);
+ u64 next_offset;
+ u64 cur = aligned_start;
+ int ret = 0;
+
+ btrfs_init_path(&path);
+
+ /* Set the whole dest all zero, so we won't need to bother holes */
+ memset(dest, 0, len);
+
+ /* Read out the leading unaligned part */
+ if (aligned_start != file_offset) {
+ ret = lookup_data_extent(root, &path, ino, aligned_start,
+ &next_offset);
+ if (ret < 0)
+ goto out;
+ if (ret == 0) {
+ /* Read the unaligned part out*/
+ fi = btrfs_item_ptr(path.nodes[0], path.slots[0],
+ struct btrfs_file_extent_item);
+ ret = read_and_truncate_page(&path, fi, file_offset,
+ round_up(file_offset, fs_info->sectorsize) -
+ file_offset, dest);
+ if (ret < 0)
+ goto out;
+ cur += fs_info->sectorsize;
+ } else {
+ /* The whole file is a hole */
+ if (!next_offset) {
+ memset(dest, 0, len);
+ return len;
+ }
+ cur = next_offset;
+ }
+ }
+
+ /* Read the aligned part */
+ while (cur < aligned_end) {
+ u64 extent_num_bytes;
+ u8 type;
+
+ btrfs_release_path(&path);
+ ret = lookup_data_extent(root, &path, ino, cur, &next_offset);
+ if (ret < 0)
+ goto out;
+ if (ret > 0) {
+ /* No next, direct exit */
+ if (!next_offset) {
+ ret = 0;
+ goto out;
+ }
+ }
+ fi = btrfs_item_ptr(path.nodes[0], path.slots[0],
+ struct btrfs_file_extent_item);
+ btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
+ type = btrfs_file_extent_type(path.nodes[0], fi);
+ if (type == BTRFS_FILE_EXTENT_INLINE) {
+ ret = btrfs_read_extent_inline(&path, fi, dest);
+ goto out;
+ }
+ /* Skip holes, as we have zeroed the dest */
+ if (type == BTRFS_FILE_EXTENT_PREALLOC ||
+ btrfs_file_extent_disk_bytenr(path.nodes[0], fi) == 0) {
+ cur = key.offset + btrfs_file_extent_num_bytes(
+ path.nodes[0], fi);
+ continue;
+ }
+
+ /* Read the remaining part of the extent */
+ extent_num_bytes = btrfs_file_extent_num_bytes(path.nodes[0],
+ fi);
+ ret = btrfs_read_extent_reg(&path, fi, cur,
+ min(extent_num_bytes, aligned_end - cur),
+ dest + cur - file_offset);
+ if (ret < 0)
+ goto out;
+ cur += min(extent_num_bytes, aligned_end - cur);
+ }
+
+ /* Read the tailing unaligned part*/
+ if (file_offset + len != aligned_end) {
+ btrfs_release_path(&path);
+ ret = lookup_data_extent(root, &path, ino, aligned_end,
+ &next_offset);
+ /* <0 is error, >0 means no extent */
+ if (ret)
+ goto out;
+ fi = btrfs_item_ptr(path.nodes[0], path.slots[0],
+ struct btrfs_file_extent_item);
+ ret = read_and_truncate_page(&path, fi, aligned_end,
+ file_offset + len - aligned_end,
+ dest + aligned_end - file_offset);
+ }
+out:
+ btrfs_release_path(&path);
+ if (ret < 0)
+ return ret;
+ return len;
+}
diff --git a/roms/u-boot/fs/btrfs/kernel-shared/btrfs_tree.h b/roms/u-boot/fs/btrfs/kernel-shared/btrfs_tree.h
new file mode 100644
index 000000000..6a76d1e45
--- /dev/null
+++ b/roms/u-boot/fs/btrfs/kernel-shared/btrfs_tree.h
@@ -0,0 +1,1333 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Copied from kernel/include/uapi/linux/btrfs_btree.h.
+ *
+ * Only modified the header.
+ */
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef __BTRFS_TREE_H__
+#define __BTRFS_TREE_H__
+
+#include <linux/types.h>
+
+#define BTRFS_MAGIC 0x4D5F53665248425FULL /* ascii _BHRfS_M, no null */
+
+/*
+ * The max metadata block size (node size).
+ *
+ * This limit is somewhat artificial. The memmove and tree block locking cost
+ * go up with larger node size.
+ */
+#define BTRFS_MAX_METADATA_BLOCKSIZE 65536
+
+/*
+ * We can actually store much bigger names, but lets not confuse the rest
+ * of linux.
+ *
+ * btrfs_dir_item::name_len follows this limitation.
+ */
+#define BTRFS_NAME_LEN 255
+
+/*
+ * Objectids start from here.
+ *
+ * Check btrfs_disk_key for the meaning of objectids.
+ */
+
+/*
+ * Root tree holds pointers to all of the tree roots.
+ * Without special mention, the root tree contains the root bytenr of all other
+ * trees, except the chunk tree and the log tree.
+ *
+ * The super block contains the root bytenr of this tree.
+ */
+#define BTRFS_ROOT_TREE_OBJECTID 1ULL
+
+/*
+ * Extent tree stores information about which extents are in use, and backrefs
+ * for each extent.
+ */
+#define BTRFS_EXTENT_TREE_OBJECTID 2ULL
+
+/*
+ * Chunk tree stores btrfs logical address -> physical address mapping.
+ *
+ * The super block contains part of chunk tree for bootstrap, and contains
+ * the root bytenr of this tree.
+ */
+#define BTRFS_CHUNK_TREE_OBJECTID 3ULL
+
+/*
+ * Device tree stores info about which areas of a given device are in use,
+ * and physical address -> btrfs logical address mapping.
+ */
+#define BTRFS_DEV_TREE_OBJECTID 4ULL
+
+/* The fs tree is the first subvolume tree, storing files and directories. */
+#define BTRFS_FS_TREE_OBJECTID 5ULL
+
+/* Shows the directory objectid inside the root tree. */
+#define BTRFS_ROOT_TREE_DIR_OBJECTID 6ULL
+
+/* Csum tree holds checksums of all the data extents. */
+#define BTRFS_CSUM_TREE_OBJECTID 7ULL
+
+/* Quota tree holds quota configuration and tracking. */
+#define BTRFS_QUOTA_TREE_OBJECTID 8ULL
+
+/* UUID tree stores items that use the BTRFS_UUID_KEY* types. */
+#define BTRFS_UUID_TREE_OBJECTID 9ULL
+
+/* Free space cache tree (v2 space cache) tracks free space in block groups. */
+#define BTRFS_FREE_SPACE_TREE_OBJECTID 10ULL
+
+/* Indicates device stats in the device tree. */
+#define BTRFS_DEV_STATS_OBJECTID 0ULL
+
+/* For storing balance parameters in the root tree. */
+#define BTRFS_BALANCE_OBJECTID -4ULL
+
+/* Orhpan objectid for tracking unlinked/truncated files. */
+#define BTRFS_ORPHAN_OBJECTID -5ULL
+
+/* Does write ahead logging to speed up fsyncs. */
+#define BTRFS_TREE_LOG_OBJECTID -6ULL
+#define BTRFS_TREE_LOG_FIXUP_OBJECTID -7ULL
+
+/* For space balancing. */
+#define BTRFS_TREE_RELOC_OBJECTID -8ULL
+#define BTRFS_DATA_RELOC_TREE_OBJECTID -9ULL
+
+/* Extent checksums, shared between the csum tree and log trees. */
+#define BTRFS_EXTENT_CSUM_OBJECTID -10ULL
+
+/* For storing free space cache (v1 space cache). */
+#define BTRFS_FREE_SPACE_OBJECTID -11ULL
+
+/* The inode number assigned to the special inode for storing free ino cache. */
+#define BTRFS_FREE_INO_OBJECTID -12ULL
+
+/* Dummy objectid represents multiple objectids. */
+#define BTRFS_MULTIPLE_OBJECTIDS -255ULL
+
+/* All files have objectids in this range. */
+#define BTRFS_FIRST_FREE_OBJECTID 256ULL
+#define BTRFS_LAST_FREE_OBJECTID -256ULL
+#define BTRFS_FIRST_CHUNK_TREE_OBJECTID 256ULL
+
+
+/*
+ * The device items go into the chunk tree.
+ *
+ * The key is in the form
+ * (BTRFS_DEV_ITEMS_OBJECTID, BTRFS_DEV_ITEM_KEY, <device_id>)
+ */
+#define BTRFS_DEV_ITEMS_OBJECTID 1ULL
+
+#define BTRFS_BTREE_INODE_OBJECTID 1
+
+#define BTRFS_EMPTY_SUBVOL_DIR_OBJECTID 2
+
+#define BTRFS_DEV_REPLACE_DEVID 0ULL
+
+/*
+ * Types start from here.
+ *
+ * Check btrfs_disk_key for details about types.
+ */
+
+/*
+ * Inode items have the data typically returned from stat and store other
+ * info about object characteristics.
+ *
+ * There is one for every file and dir in the FS.
+ */
+#define BTRFS_INODE_ITEM_KEY 1
+/* reserve 2-11 close to the inode for later flexibility */
+#define BTRFS_INODE_REF_KEY 12
+#define BTRFS_INODE_EXTREF_KEY 13
+#define BTRFS_XATTR_ITEM_KEY 24
+#define BTRFS_ORPHAN_ITEM_KEY 48
+
+/*
+ * Dir items are the name -> inode pointers in a directory.
+ *
+ * There is one for every name in a directory.
+ */
+#define BTRFS_DIR_LOG_ITEM_KEY 60
+#define BTRFS_DIR_LOG_INDEX_KEY 72
+#define BTRFS_DIR_ITEM_KEY 84
+#define BTRFS_DIR_INDEX_KEY 96
+
+/* Stores info (position, size ...) about a data extent of a file */
+#define BTRFS_EXTENT_DATA_KEY 108
+
+/*
+ * Extent csums are stored in a separate tree and hold csums for
+ * an entire extent on disk.
+ */
+#define BTRFS_EXTENT_CSUM_KEY 128
+
+/*
+ * Root items point to tree roots.
+ *
+ * They are typically in the root tree used by the super block to find all the
+ * other trees.
+ */
+#define BTRFS_ROOT_ITEM_KEY 132
+
+/*
+ * Root backrefs tie subvols and snapshots to the directory entries that
+ * reference them.
+ */
+#define BTRFS_ROOT_BACKREF_KEY 144
+
+/*
+ * Root refs make a fast index for listing all of the snapshots and
+ * subvolumes referenced by a given root. They point directly to the
+ * directory item in the root that references the subvol.
+ */
+#define BTRFS_ROOT_REF_KEY 156
+
+/*
+ * Extent items are in the extent tree.
+ *
+ * These record which blocks are used, and how many references there are.
+ */
+#define BTRFS_EXTENT_ITEM_KEY 168
+
+/*
+ * The same as the BTRFS_EXTENT_ITEM_KEY, except it's metadata we already know
+ * the length, so we save the level in key->offset instead of the length.
+ */
+#define BTRFS_METADATA_ITEM_KEY 169
+
+#define BTRFS_TREE_BLOCK_REF_KEY 176
+
+#define BTRFS_EXTENT_DATA_REF_KEY 178
+
+#define BTRFS_EXTENT_REF_V0_KEY 180
+
+#define BTRFS_SHARED_BLOCK_REF_KEY 182
+
+#define BTRFS_SHARED_DATA_REF_KEY 184
+
+/*
+ * Block groups give us hints into the extent allocation trees.
+ *
+ * Stores how many free space there is in a block group.
+ */
+#define BTRFS_BLOCK_GROUP_ITEM_KEY 192
+
+/*
+ * Every block group is represented in the free space tree by a free space info
+ * item, which stores some accounting information. It is keyed on
+ * (block_group_start, FREE_SPACE_INFO, block_group_length).
+ */
+#define BTRFS_FREE_SPACE_INFO_KEY 198
+
+/*
+ * A free space extent tracks an extent of space that is free in a block group.
+ * It is keyed on (start, FREE_SPACE_EXTENT, length).
+ */
+#define BTRFS_FREE_SPACE_EXTENT_KEY 199
+
+/*
+ * When a block group becomes very fragmented, we convert it to use bitmaps
+ * instead of extents.
+ *
+ * A free space bitmap is keyed on (start, FREE_SPACE_BITMAP, length).
+ * The corresponding item is a bitmap with (length / sectorsize) bits.
+ */
+#define BTRFS_FREE_SPACE_BITMAP_KEY 200
+
+#define BTRFS_DEV_EXTENT_KEY 204
+#define BTRFS_DEV_ITEM_KEY 216
+#define BTRFS_CHUNK_ITEM_KEY 228
+
+/*
+ * Records the overall state of the qgroups.
+ *
+ * There's only one instance of this key present,
+ * (0, BTRFS_QGROUP_STATUS_KEY, 0)
+ */
+#define BTRFS_QGROUP_STATUS_KEY 240
+/*
+ * Records the currently used space of the qgroup.
+ *
+ * One key per qgroup, (0, BTRFS_QGROUP_INFO_KEY, qgroupid).
+ */
+#define BTRFS_QGROUP_INFO_KEY 242
+
+/*
+ * Contains the user configured limits for the qgroup.
+ *
+ * One key per qgroup, (0, BTRFS_QGROUP_LIMIT_KEY, qgroupid).
+ */
+#define BTRFS_QGROUP_LIMIT_KEY 244
+
+/*
+ * Records the child-parent relationship of qgroups. For
+ * each relation, 2 keys are present:
+ * (childid, BTRFS_QGROUP_RELATION_KEY, parentid)
+ * (parentid, BTRFS_QGROUP_RELATION_KEY, childid)
+ */
+#define BTRFS_QGROUP_RELATION_KEY 246
+
+/* Obsolete name, see BTRFS_TEMPORARY_ITEM_KEY. */
+#define BTRFS_BALANCE_ITEM_KEY 248
+
+/*
+ * The key type for tree items that are stored persistently, but do not need to
+ * exist for extended period of time. The items can exist in any tree.
+ *
+ * [subtype, BTRFS_TEMPORARY_ITEM_KEY, data]
+ *
+ * Existing items:
+ *
+ * - balance status item
+ * (BTRFS_BALANCE_OBJECTID, BTRFS_TEMPORARY_ITEM_KEY, 0)
+ */
+#define BTRFS_TEMPORARY_ITEM_KEY 248
+
+/* Obsolete name, see BTRFS_PERSISTENT_ITEM_KEY */
+#define BTRFS_DEV_STATS_KEY 249
+
+/*
+ * The key type for tree items that are stored persistently and usually exist
+ * for a long period, eg. filesystem lifetime. The item kinds can be status
+ * information, stats or preference values. The item can exist in any tree.
+ *
+ * [subtype, BTRFS_PERSISTENT_ITEM_KEY, data]
+ *
+ * Existing items:
+ *
+ * - device statistics, store IO stats in the device tree, one key for all
+ * stats
+ * (BTRFS_DEV_STATS_OBJECTID, BTRFS_DEV_STATS_KEY, 0)
+ */
+#define BTRFS_PERSISTENT_ITEM_KEY 249
+
+/*
+ * Persistently stores the device replace state in the device tree.
+ *
+ * The key is built like this: (0, BTRFS_DEV_REPLACE_KEY, 0).
+ */
+#define BTRFS_DEV_REPLACE_KEY 250
+
+/*
+ * Stores items that allow to quickly map UUIDs to something else.
+ *
+ * These items are part of the filesystem UUID tree.
+ * The key is built like this:
+ * (UUID_upper_64_bits, BTRFS_UUID_KEY*, UUID_lower_64_bits).
+ */
+#define BTRFS_UUID_KEY_SUBVOL 251 /* for UUIDs assigned to subvols */
+#define BTRFS_UUID_KEY_RECEIVED_SUBVOL 252 /* for UUIDs assigned to
+ * received subvols */
+
+/*
+ * String items are for debugging.
+ *
+ * They just store a short string of data in the FS.
+ */
+#define BTRFS_STRING_ITEM_KEY 253
+
+
+
+/* 32 bytes in various csum fields */
+#define BTRFS_CSUM_SIZE 32
+
+/* Csum types */
+enum btrfs_csum_type {
+ BTRFS_CSUM_TYPE_CRC32 = 0,
+ BTRFS_CSUM_TYPE_XXHASH = 1,
+ BTRFS_CSUM_TYPE_SHA256 = 2,
+ BTRFS_CSUM_TYPE_BLAKE2 = 3,
+};
+
+/*
+ * Flags definitions for directory entry item type.
+ *
+ * Used by:
+ * struct btrfs_dir_item.type
+ *
+ * Values 0..7 must match common file type values in fs_types.h.
+ */
+#define BTRFS_FT_UNKNOWN 0
+#define BTRFS_FT_REG_FILE 1
+#define BTRFS_FT_DIR 2
+#define BTRFS_FT_CHRDEV 3
+#define BTRFS_FT_BLKDEV 4
+#define BTRFS_FT_FIFO 5
+#define BTRFS_FT_SOCK 6
+#define BTRFS_FT_SYMLINK 7
+#define BTRFS_FT_XATTR 8
+#define BTRFS_FT_MAX 9
+
+#define BTRFS_FSID_SIZE 16
+#define BTRFS_UUID_SIZE 16
+
+/*
+ * The key defines the order in the tree, and so it also defines (optimal)
+ * block layout.
+ *
+ * Objectid and offset are interpreted based on type.
+ * While normally for objectid, it either represents a root number, or an
+ * inode number.
+ *
+ * Type tells us things about the object, and is a kind of stream selector.
+ * Check the following URL for full references about btrfs_disk_key/btrfs_key:
+ * https://btrfs.wiki.kernel.org/index.php/Btree_Items
+ *
+ * btrfs_disk_key is in disk byte order. struct btrfs_key is always
+ * in cpu native order. Otherwise they are identical and their sizes
+ * should be the same (ie both packed)
+ */
+struct btrfs_disk_key {
+ __le64 objectid;
+ __u8 type;
+ __le64 offset;
+} __attribute__ ((__packed__));
+
+struct btrfs_key {
+ __u64 objectid;
+ __u8 type;
+ __u64 offset;
+} __attribute__ ((__packed__));
+
+struct btrfs_dev_item {
+ /* The internal btrfs device id */
+ __le64 devid;
+
+ /* Size of the device */
+ __le64 total_bytes;
+
+ /* Bytes used */
+ __le64 bytes_used;
+
+ /* Optimal io alignment for this device */
+ __le32 io_align;
+
+ /* Optimal io width for this device */
+ __le32 io_width;
+
+ /* Minimal io size for this device */
+ __le32 sector_size;
+
+ /* Type and info about this device */
+ __le64 type;
+
+ /* Expected generation for this device */
+ __le64 generation;
+
+ /*
+ * Starting byte of this partition on the device,
+ * to allow for stripe alignment in the future.
+ */
+ __le64 start_offset;
+
+ /* Grouping information for allocation decisions */
+ __le32 dev_group;
+
+ /* Optimal seek speed 0-100 where 100 is fastest */
+ __u8 seek_speed;
+
+ /* Optimal bandwidth 0-100 where 100 is fastest */
+ __u8 bandwidth;
+
+ /* Btrfs generated uuid for this device */
+ __u8 uuid[BTRFS_UUID_SIZE];
+
+ /* UUID of FS who owns this device */
+ __u8 fsid[BTRFS_UUID_SIZE];
+} __attribute__ ((__packed__));
+
+struct btrfs_stripe {
+ __le64 devid;
+ __le64 offset;
+ __u8 dev_uuid[BTRFS_UUID_SIZE];
+} __attribute__ ((__packed__));
+
+struct btrfs_chunk {
+ /* Size of this chunk in bytes */
+ __le64 length;
+
+ /* Objectid of the root referencing this chunk */
+ __le64 owner;
+
+ __le64 stripe_len;
+ __le64 type;
+
+ /* Optimal io alignment for this chunk */
+ __le32 io_align;
+
+ /* Optimal io width for this chunk */
+ __le32 io_width;
+
+ /* Minimal io size for this chunk */
+ __le32 sector_size;
+
+ /*
+ * 2^16 stripes is quite a lot, a second limit is the size of a single
+ * item in the btree.
+ */
+ __le16 num_stripes;
+
+ /* Sub stripes only matter for raid10 */
+ __le16 sub_stripes;
+ struct btrfs_stripe stripe;
+ /* additional stripes go here */
+} __attribute__ ((__packed__));
+
+#define BTRFS_FREE_SPACE_EXTENT 1
+#define BTRFS_FREE_SPACE_BITMAP 2
+
+struct btrfs_free_space_entry {
+ __le64 offset;
+ __le64 bytes;
+ __u8 type;
+} __attribute__ ((__packed__));
+
+struct btrfs_free_space_header {
+ struct btrfs_disk_key location;
+ __le64 generation;
+ __le64 num_entries;
+ __le64 num_bitmaps;
+} __attribute__ ((__packed__));
+
+#define BTRFS_HEADER_FLAG_WRITTEN (1ULL << 0)
+#define BTRFS_HEADER_FLAG_RELOC (1ULL << 1)
+
+/* Super block flags */
+/* Errors detected */
+#define BTRFS_SUPER_FLAG_ERROR (1ULL << 2)
+
+#define BTRFS_SUPER_FLAG_SEEDING (1ULL << 32)
+#define BTRFS_SUPER_FLAG_METADUMP (1ULL << 33)
+#define BTRFS_SUPER_FLAG_METADUMP_V2 (1ULL << 34)
+#define BTRFS_SUPER_FLAG_CHANGING_FSID (1ULL << 35)
+#define BTRFS_SUPER_FLAG_CHANGING_FSID_V2 (1ULL << 36)
+
+
+/*
+ * Items in the extent tree are used to record the objectid of the
+ * owner of the block and the number of references.
+ */
+struct btrfs_extent_item {
+ __le64 refs;
+ __le64 generation;
+ __le64 flags;
+} __attribute__ ((__packed__));
+
+struct btrfs_extent_item_v0 {
+ __le32 refs;
+} __attribute__ ((__packed__));
+
+
+#define BTRFS_EXTENT_FLAG_DATA (1ULL << 0)
+#define BTRFS_EXTENT_FLAG_TREE_BLOCK (1ULL << 1)
+
+/* Use full backrefs for extent pointers in the block */
+#define BTRFS_BLOCK_FLAG_FULL_BACKREF (1ULL << 8)
+
+/*
+ * This flag is only used internally by scrub and may be changed at any time
+ * it is only declared here to avoid collisions.
+ */
+#define BTRFS_EXTENT_FLAG_SUPER (1ULL << 48)
+
+struct btrfs_tree_block_info {
+ struct btrfs_disk_key key;
+ __u8 level;
+} __attribute__ ((__packed__));
+
+struct btrfs_extent_data_ref {
+ __le64 root;
+ __le64 objectid;
+ __le64 offset;
+ __le32 count;
+} __attribute__ ((__packed__));
+
+struct btrfs_shared_data_ref {
+ __le32 count;
+} __attribute__ ((__packed__));
+
+struct btrfs_extent_inline_ref {
+ __u8 type;
+ __le64 offset;
+} __attribute__ ((__packed__));
+
+/* Old style backrefs item */
+struct btrfs_extent_ref_v0 {
+ __le64 root;
+ __le64 generation;
+ __le64 objectid;
+ __le32 count;
+} __attribute__ ((__packed__));
+
+
+/* Dev extents record used space on individual devices.
+ *
+ * The owner field points back to the chunk allocation mapping tree that
+ * allocated the extent.
+ * The chunk tree uuid field is a way to double check the owner.
+ */
+struct btrfs_dev_extent {
+ __le64 chunk_tree;
+ __le64 chunk_objectid;
+ __le64 chunk_offset;
+ __le64 length;
+ __u8 chunk_tree_uuid[BTRFS_UUID_SIZE];
+} __attribute__ ((__packed__));
+
+struct btrfs_inode_ref {
+ __le64 index;
+ __le16 name_len;
+ /* Name goes here */
+} __attribute__ ((__packed__));
+
+struct btrfs_inode_extref {
+ __le64 parent_objectid;
+ __le64 index;
+ __le16 name_len;
+ __u8 name[0];
+ /* Name goes here */
+} __attribute__ ((__packed__));
+
+struct btrfs_timespec {
+ __le64 sec;
+ __le32 nsec;
+} __attribute__ ((__packed__));
+
+/* Inode flags */
+#define BTRFS_INODE_NODATASUM (1 << 0)
+#define BTRFS_INODE_NODATACOW (1 << 1)
+#define BTRFS_INODE_READONLY (1 << 2)
+#define BTRFS_INODE_NOCOMPRESS (1 << 3)
+#define BTRFS_INODE_PREALLOC (1 << 4)
+#define BTRFS_INODE_SYNC (1 << 5)
+#define BTRFS_INODE_IMMUTABLE (1 << 6)
+#define BTRFS_INODE_APPEND (1 << 7)
+#define BTRFS_INODE_NODUMP (1 << 8)
+#define BTRFS_INODE_NOATIME (1 << 9)
+#define BTRFS_INODE_DIRSYNC (1 << 10)
+#define BTRFS_INODE_COMPRESS (1 << 11)
+
+#define BTRFS_INODE_ROOT_ITEM_INIT (1 << 31)
+
+#define BTRFS_INODE_FLAG_MASK \
+ (BTRFS_INODE_NODATASUM | \
+ BTRFS_INODE_NODATACOW | \
+ BTRFS_INODE_READONLY | \
+ BTRFS_INODE_NOCOMPRESS | \
+ BTRFS_INODE_PREALLOC | \
+ BTRFS_INODE_SYNC | \
+ BTRFS_INODE_IMMUTABLE | \
+ BTRFS_INODE_APPEND | \
+ BTRFS_INODE_NODUMP | \
+ BTRFS_INODE_NOATIME | \
+ BTRFS_INODE_DIRSYNC | \
+ BTRFS_INODE_COMPRESS | \
+ BTRFS_INODE_ROOT_ITEM_INIT)
+
+struct btrfs_inode_item {
+ /* Nfs style generation number */
+ __le64 generation;
+ /* Transid that last touched this inode */
+ __le64 transid;
+ __le64 size;
+ __le64 nbytes;
+ __le64 block_group;
+ __le32 nlink;
+ __le32 uid;
+ __le32 gid;
+ __le32 mode;
+ __le64 rdev;
+ __le64 flags;
+
+ /* Modification sequence number for NFS */
+ __le64 sequence;
+
+ /*
+ * A little future expansion, for more than this we can just grow the
+ * inode item and version it
+ */
+ __le64 reserved[4];
+ struct btrfs_timespec atime;
+ struct btrfs_timespec ctime;
+ struct btrfs_timespec mtime;
+ struct btrfs_timespec otime;
+} __attribute__ ((__packed__));
+
+struct btrfs_dir_log_item {
+ __le64 end;
+} __attribute__ ((__packed__));
+
+struct btrfs_dir_item {
+ struct btrfs_disk_key location;
+ __le64 transid;
+ __le16 data_len;
+ __le16 name_len;
+ __u8 type;
+} __attribute__ ((__packed__));
+
+#define BTRFS_ROOT_SUBVOL_RDONLY (1ULL << 0)
+
+/*
+ * Internal in-memory flag that a subvolume has been marked for deletion but
+ * still visible as a directory
+ */
+#define BTRFS_ROOT_SUBVOL_DEAD (1ULL << 48)
+
+struct btrfs_root_item {
+ struct btrfs_inode_item inode;
+ __le64 generation;
+ __le64 root_dirid;
+ __le64 bytenr;
+ __le64 byte_limit;
+ __le64 bytes_used;
+ __le64 last_snapshot;
+ __le64 flags;
+ __le32 refs;
+ struct btrfs_disk_key drop_progress;
+ __u8 drop_level;
+ __u8 level;
+
+ /*
+ * The following fields appear after subvol_uuids+subvol_times
+ * were introduced.
+ */
+
+ /*
+ * This generation number is used to test if the new fields are valid
+ * and up to date while reading the root item. Every time the root item
+ * is written out, the "generation" field is copied into this field. If
+ * anyone ever mounted the fs with an older kernel, we will have
+ * mismatching generation values here and thus must invalidate the
+ * new fields. See btrfs_update_root and btrfs_find_last_root for
+ * details.
+ * The offset of generation_v2 is also used as the start for the memset
+ * when invalidating the fields.
+ */
+ __le64 generation_v2;
+ __u8 uuid[BTRFS_UUID_SIZE];
+ __u8 parent_uuid[BTRFS_UUID_SIZE];
+ __u8 received_uuid[BTRFS_UUID_SIZE];
+ __le64 ctransid; /* Updated when an inode changes */
+ __le64 otransid; /* Trans when created */
+ __le64 stransid; /* Trans when sent. Non-zero for received subvol. */
+ __le64 rtransid; /* Trans when received. Non-zero for received subvol.*/
+ struct btrfs_timespec ctime;
+ struct btrfs_timespec otime;
+ struct btrfs_timespec stime;
+ struct btrfs_timespec rtime;
+ __le64 reserved[8]; /* For future */
+} __attribute__ ((__packed__));
+
+/* This is used for both forward and backward root refs */
+struct btrfs_root_ref {
+ __le64 dirid;
+ __le64 sequence;
+ __le16 name_len;
+} __attribute__ ((__packed__));
+
+struct btrfs_disk_balance_args {
+ /*
+ * Profiles to operate on.
+ *
+ * SINGLE is denoted by BTRFS_AVAIL_ALLOC_BIT_SINGLE.
+ */
+ __le64 profiles;
+
+ /*
+ * Usage filter
+ * BTRFS_BALANCE_ARGS_USAGE with a single value means '0..N'
+ * BTRFS_BALANCE_ARGS_USAGE_RANGE - range syntax, min..max
+ */
+ union {
+ __le64 usage;
+ struct {
+ __le32 usage_min;
+ __le32 usage_max;
+ };
+ };
+
+ /* Devid filter */
+ __le64 devid;
+
+ /* Devid subset filter [pstart..pend) */
+ __le64 pstart;
+ __le64 pend;
+
+ /* Btrfs virtual address space subset filter [vstart..vend) */
+ __le64 vstart;
+ __le64 vend;
+
+ /*
+ * Profile to convert to.
+ *
+ * SINGLE is denoted by BTRFS_AVAIL_ALLOC_BIT_SINGLE.
+ */
+ __le64 target;
+
+ /* BTRFS_BALANCE_ARGS_* */
+ __le64 flags;
+
+ /*
+ * BTRFS_BALANCE_ARGS_LIMIT with value 'limit'.
+ * BTRFS_BALANCE_ARGS_LIMIT_RANGE - the extend version can use minimum
+ * and maximum.
+ */
+ union {
+ __le64 limit;
+ struct {
+ __le32 limit_min;
+ __le32 limit_max;
+ };
+ };
+
+ /*
+ * Process chunks that cross stripes_min..stripes_max devices,
+ * BTRFS_BALANCE_ARGS_STRIPES_RANGE.
+ */
+ __le32 stripes_min;
+ __le32 stripes_max;
+
+ __le64 unused[6];
+} __attribute__ ((__packed__));
+
+/*
+ * Stores balance parameters to disk so that balance can be properly
+ * resumed after crash or unmount.
+ */
+struct btrfs_balance_item {
+ /* BTRFS_BALANCE_* */
+ __le64 flags;
+
+ struct btrfs_disk_balance_args data;
+ struct btrfs_disk_balance_args meta;
+ struct btrfs_disk_balance_args sys;
+
+ __le64 unused[4];
+} __attribute__ ((__packed__));
+
+enum {
+ BTRFS_FILE_EXTENT_INLINE = 0,
+ BTRFS_FILE_EXTENT_REG = 1,
+ BTRFS_FILE_EXTENT_PREALLOC = 2,
+ BTRFS_NR_FILE_EXTENT_TYPES = 3,
+};
+
+enum btrfs_compression_type {
+ BTRFS_COMPRESS_NONE = 0,
+ BTRFS_COMPRESS_ZLIB = 1,
+ BTRFS_COMPRESS_LZO = 2,
+ BTRFS_COMPRESS_ZSTD = 3,
+ BTRFS_NR_COMPRESS_TYPES = 4,
+};
+
+struct btrfs_file_extent_item {
+ /* Transaction id that created this extent */
+ __le64 generation;
+ /*
+ * Max number of bytes to hold this extent in ram.
+ *
+ * When we split a compressed extent we can't know how big each of the
+ * resulting pieces will be. So, this is an upper limit on the size of
+ * the extent in ram instead of an exact limit.
+ */
+ __le64 ram_bytes;
+
+ /*
+ * 32 bits for the various ways we might encode the data,
+ * including compression and encryption. If any of these
+ * are set to something a given disk format doesn't understand
+ * it is treated like an incompat flag for reading and writing,
+ * but not for stat.
+ */
+ __u8 compression;
+ __u8 encryption;
+ __le16 other_encoding; /* Spare for later use */
+
+ /* Are we inline data or a real extent? */
+ __u8 type;
+
+ /*
+ * Disk space consumed by the extent, checksum blocks are not included
+ * in these numbers
+ *
+ * At this offset in the structure, the inline extent data start.
+ */
+ __le64 disk_bytenr;
+ __le64 disk_num_bytes;
+
+ /*
+ * The logical offset inside the file extent.
+ *
+ * This allows a file extent to point into the middle of an existing
+ * extent on disk, sharing it between two snapshots (useful if some
+ * bytes in the middle of the extent have changed).
+ */
+ __le64 offset;
+
+ /*
+ * The logical number of bytes this file extent is referencing (no
+ * csums included).
+ *
+ * This always reflects the size uncompressed and without encoding.
+ */
+ __le64 num_bytes;
+
+} __attribute__ ((__packed__));
+
+struct btrfs_csum_item {
+ __u8 csum;
+} __attribute__ ((__packed__));
+
+enum btrfs_dev_stat_values {
+ /* Disk I/O failure stats */
+ BTRFS_DEV_STAT_WRITE_ERRS, /* EIO or EREMOTEIO from lower layers */
+ BTRFS_DEV_STAT_READ_ERRS, /* EIO or EREMOTEIO from lower layers */
+ BTRFS_DEV_STAT_FLUSH_ERRS, /* EIO or EREMOTEIO from lower layers */
+
+ /* Stats for indirect indications for I/O failures */
+ BTRFS_DEV_STAT_CORRUPTION_ERRS, /* Checksum error, bytenr error or
+ * contents is illegal: this is an
+ * indication that the block was damaged
+ * during read or write, or written to
+ * wrong location or read from wrong
+ * location */
+ BTRFS_DEV_STAT_GENERATION_ERRS, /* An indication that blocks have not
+ * been written */
+
+ BTRFS_DEV_STAT_VALUES_MAX
+};
+
+struct btrfs_dev_stats_item {
+ /*
+ * Grow this item struct at the end for future enhancements and keep
+ * the existing values unchanged.
+ */
+ __le64 values[BTRFS_DEV_STAT_VALUES_MAX];
+} __attribute__ ((__packed__));
+
+#define BTRFS_DEV_REPLACE_ITEM_CONT_READING_FROM_SRCDEV_MODE_ALWAYS 0
+#define BTRFS_DEV_REPLACE_ITEM_CONT_READING_FROM_SRCDEV_MODE_AVOID 1
+
+struct btrfs_dev_replace_item {
+ /*
+ * Grow this item struct at the end for future enhancements and keep
+ * the existing values unchanged.
+ */
+ __le64 src_devid;
+ __le64 cursor_left;
+ __le64 cursor_right;
+ __le64 cont_reading_from_srcdev_mode;
+
+ __le64 replace_state;
+ __le64 time_started;
+ __le64 time_stopped;
+ __le64 num_write_errors;
+ __le64 num_uncorrectable_read_errors;
+} __attribute__ ((__packed__));
+
+/* Different types of block groups (and chunks) */
+#define BTRFS_BLOCK_GROUP_DATA (1ULL << 0)
+#define BTRFS_BLOCK_GROUP_SYSTEM (1ULL << 1)
+#define BTRFS_BLOCK_GROUP_METADATA (1ULL << 2)
+#define BTRFS_BLOCK_GROUP_RAID0 (1ULL << 3)
+#define BTRFS_BLOCK_GROUP_RAID1 (1ULL << 4)
+#define BTRFS_BLOCK_GROUP_DUP (1ULL << 5)
+#define BTRFS_BLOCK_GROUP_RAID10 (1ULL << 6)
+#define BTRFS_BLOCK_GROUP_RAID5 (1ULL << 7)
+#define BTRFS_BLOCK_GROUP_RAID6 (1ULL << 8)
+#define BTRFS_BLOCK_GROUP_RAID1C3 (1ULL << 9)
+#define BTRFS_BLOCK_GROUP_RAID1C4 (1ULL << 10)
+#define BTRFS_BLOCK_GROUP_RESERVED (BTRFS_AVAIL_ALLOC_BIT_SINGLE | \
+ BTRFS_SPACE_INFO_GLOBAL_RSV)
+
+enum btrfs_raid_types {
+ BTRFS_RAID_RAID10,
+ BTRFS_RAID_RAID1,
+ BTRFS_RAID_DUP,
+ BTRFS_RAID_RAID0,
+ BTRFS_RAID_SINGLE,
+ BTRFS_RAID_RAID5,
+ BTRFS_RAID_RAID6,
+ BTRFS_RAID_RAID1C3,
+ BTRFS_RAID_RAID1C4,
+ BTRFS_NR_RAID_TYPES
+};
+
+#define BTRFS_BLOCK_GROUP_TYPE_MASK (BTRFS_BLOCK_GROUP_DATA | \
+ BTRFS_BLOCK_GROUP_SYSTEM | \
+ BTRFS_BLOCK_GROUP_METADATA)
+
+#define BTRFS_BLOCK_GROUP_PROFILE_MASK (BTRFS_BLOCK_GROUP_RAID0 | \
+ BTRFS_BLOCK_GROUP_RAID1 | \
+ BTRFS_BLOCK_GROUP_RAID1C3 | \
+ BTRFS_BLOCK_GROUP_RAID1C4 | \
+ BTRFS_BLOCK_GROUP_RAID5 | \
+ BTRFS_BLOCK_GROUP_RAID6 | \
+ BTRFS_BLOCK_GROUP_DUP | \
+ BTRFS_BLOCK_GROUP_RAID10)
+#define BTRFS_BLOCK_GROUP_RAID56_MASK (BTRFS_BLOCK_GROUP_RAID5 | \
+ BTRFS_BLOCK_GROUP_RAID6)
+
+#define BTRFS_BLOCK_GROUP_RAID1_MASK (BTRFS_BLOCK_GROUP_RAID1 | \
+ BTRFS_BLOCK_GROUP_RAID1C3 | \
+ BTRFS_BLOCK_GROUP_RAID1C4)
+
+/*
+ * We need a bit for restriper to be able to tell when chunks of type
+ * SINGLE are available. This "extended" profile format is used in
+ * fs_info->avail_*_alloc_bits (in-memory) and balance item fields
+ * (on-disk). The corresponding on-disk bit in chunk.type is reserved
+ * to avoid remappings between two formats in future.
+ */
+#define BTRFS_AVAIL_ALLOC_BIT_SINGLE (1ULL << 48)
+
+/*
+ * A fake block group type that is used to communicate global block reserve
+ * size to userspace via the SPACE_INFO ioctl.
+ */
+#define BTRFS_SPACE_INFO_GLOBAL_RSV (1ULL << 49)
+
+#define BTRFS_EXTENDED_PROFILE_MASK (BTRFS_BLOCK_GROUP_PROFILE_MASK | \
+ BTRFS_AVAIL_ALLOC_BIT_SINGLE)
+
+static inline __u64 chunk_to_extended(__u64 flags)
+{
+ if ((flags & BTRFS_BLOCK_GROUP_PROFILE_MASK) == 0)
+ flags |= BTRFS_AVAIL_ALLOC_BIT_SINGLE;
+
+ return flags;
+}
+static inline __u64 extended_to_chunk(__u64 flags)
+{
+ return flags & ~BTRFS_AVAIL_ALLOC_BIT_SINGLE;
+}
+
+struct btrfs_block_group_item {
+ __le64 used;
+ __le64 chunk_objectid;
+ __le64 flags;
+} __attribute__ ((__packed__));
+
+struct btrfs_free_space_info {
+ __le32 extent_count;
+ __le32 flags;
+} __attribute__ ((__packed__));
+
+#define BTRFS_FREE_SPACE_USING_BITMAPS (1ULL << 0)
+
+#define BTRFS_QGROUP_LEVEL_SHIFT 48
+static inline __u64 btrfs_qgroup_level(__u64 qgroupid)
+{
+ return qgroupid >> BTRFS_QGROUP_LEVEL_SHIFT;
+}
+
+/* Is subvolume quota turned on? */
+#define BTRFS_QGROUP_STATUS_FLAG_ON (1ULL << 0)
+
+/* Is qgroup rescan running? */
+#define BTRFS_QGROUP_STATUS_FLAG_RESCAN (1ULL << 1)
+
+/*
+ * Some qgroup entries are known to be out of date, either because the
+ * configuration has changed in a way that makes a rescan necessary, or
+ * because the fs has been mounted with a non-qgroup-aware version.
+ */
+#define BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT (1ULL << 2)
+
+#define BTRFS_QGROUP_STATUS_VERSION 1
+
+struct btrfs_qgroup_status_item {
+ __le64 version;
+ /*
+ * The generation is updated during every commit. As older
+ * versions of btrfs are not aware of qgroups, it will be
+ * possible to detect inconsistencies by checking the
+ * generation on mount time.
+ */
+ __le64 generation;
+
+ /* Flag definitions see above */
+ __le64 flags;
+
+ /*
+ * Only used during scanning to record the progress of the scan.
+ * It contains a logical address.
+ */
+ __le64 rescan;
+} __attribute__ ((__packed__));
+
+struct btrfs_qgroup_info_item {
+ __le64 generation;
+ __le64 rfer;
+ __le64 rfer_cmpr;
+ __le64 excl;
+ __le64 excl_cmpr;
+} __attribute__ ((__packed__));
+
+/*
+ * Flags definition for qgroup limits
+ *
+ * Used by:
+ * struct btrfs_qgroup_limit.flags
+ * struct btrfs_qgroup_limit_item.flags
+ */
+#define BTRFS_QGROUP_LIMIT_MAX_RFER (1ULL << 0)
+#define BTRFS_QGROUP_LIMIT_MAX_EXCL (1ULL << 1)
+#define BTRFS_QGROUP_LIMIT_RSV_RFER (1ULL << 2)
+#define BTRFS_QGROUP_LIMIT_RSV_EXCL (1ULL << 3)
+#define BTRFS_QGROUP_LIMIT_RFER_CMPR (1ULL << 4)
+#define BTRFS_QGROUP_LIMIT_EXCL_CMPR (1ULL << 5)
+
+struct btrfs_qgroup_limit_item {
+ /* Only updated when any of the other values change. */
+ __le64 flags;
+ __le64 max_rfer;
+ __le64 max_excl;
+ __le64 rsv_rfer;
+ __le64 rsv_excl;
+} __attribute__ ((__packed__));
+
+/*
+ * Just in case we somehow lose the roots and are not able to mount,
+ * we store an array of the roots from previous transactions in the super.
+ */
+#define BTRFS_NUM_BACKUP_ROOTS 4
+struct btrfs_root_backup {
+ __le64 tree_root;
+ __le64 tree_root_gen;
+
+ __le64 chunk_root;
+ __le64 chunk_root_gen;
+
+ __le64 extent_root;
+ __le64 extent_root_gen;
+
+ __le64 fs_root;
+ __le64 fs_root_gen;
+
+ __le64 dev_root;
+ __le64 dev_root_gen;
+
+ __le64 csum_root;
+ __le64 csum_root_gen;
+
+ __le64 total_bytes;
+ __le64 bytes_used;
+ __le64 num_devices;
+ /* future */
+ __le64 unused_64[4];
+
+ u8 tree_root_level;
+ u8 chunk_root_level;
+ u8 extent_root_level;
+ u8 fs_root_level;
+ u8 dev_root_level;
+ u8 csum_root_level;
+ /* future and to align */
+ u8 unused_8[10];
+} __attribute__ ((__packed__));
+
+/*
+ * This is a very generous portion of the super block, giving us room to
+ * translate 14 chunks with 3 stripes each.
+ */
+#define BTRFS_SYSTEM_CHUNK_ARRAY_SIZE 2048
+
+#define BTRFS_LABEL_SIZE 256
+
+/* The super block basically lists the main trees of the FS. */
+struct btrfs_super_block {
+ /* The first 4 fields must match struct btrfs_header */
+ u8 csum[BTRFS_CSUM_SIZE];
+ /* FS specific UUID, visible to user */
+ u8 fsid[BTRFS_FSID_SIZE];
+ __le64 bytenr; /* this block number */
+ __le64 flags;
+
+ /* Allowed to be different from the btrfs_header from here own down. */
+ __le64 magic;
+ __le64 generation;
+ __le64 root;
+ __le64 chunk_root;
+ __le64 log_root;
+
+ /* This will help find the new super based on the log root. */
+ __le64 log_root_transid;
+ __le64 total_bytes;
+ __le64 bytes_used;
+ __le64 root_dir_objectid;
+ __le64 num_devices;
+ __le32 sectorsize;
+ __le32 nodesize;
+ __le32 __unused_leafsize;
+ __le32 stripesize;
+ __le32 sys_chunk_array_size;
+ __le64 chunk_root_generation;
+ __le64 compat_flags;
+ __le64 compat_ro_flags;
+ __le64 incompat_flags;
+ __le16 csum_type;
+ u8 root_level;
+ u8 chunk_root_level;
+ u8 log_root_level;
+ struct btrfs_dev_item dev_item;
+
+ char label[BTRFS_LABEL_SIZE];
+
+ __le64 cache_generation;
+ __le64 uuid_tree_generation;
+
+ /* The UUID written into btree blocks */
+ u8 metadata_uuid[BTRFS_FSID_SIZE];
+
+ /* Future expansion */
+ __le64 reserved[28];
+ u8 sys_chunk_array[BTRFS_SYSTEM_CHUNK_ARRAY_SIZE];
+ struct btrfs_root_backup super_roots[BTRFS_NUM_BACKUP_ROOTS];
+} __attribute__ ((__packed__));
+
+/*
+ * Feature flags
+ *
+ * Used by:
+ * struct btrfs_super_block::(compat|compat_ro|incompat)_flags
+ * struct btrfs_ioctl_feature_flags
+ */
+#define BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE (1ULL << 0)
+
+/*
+ * Older kernels (< 4.9) on big-endian systems produced broken free space tree
+ * bitmaps, and btrfs-progs also used to corrupt the free space tree (versions
+ * < 4.7.3). If this bit is clear, then the free space tree cannot be trusted.
+ * btrfs-progs can also intentionally clear this bit to ask the kernel to
+ * rebuild the free space tree, however this might not work on older kernels
+ * that do not know about this bit. If not sure, clear the cache manually on
+ * first mount when booting older kernel versions.
+ */
+#define BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE_VALID (1ULL << 1)
+
+#define BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF (1ULL << 0)
+#define BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL (1ULL << 1)
+#define BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS (1ULL << 2)
+#define BTRFS_FEATURE_INCOMPAT_COMPRESS_LZO (1ULL << 3)
+#define BTRFS_FEATURE_INCOMPAT_COMPRESS_ZSTD (1ULL << 4)
+
+/*
+ * Older kernels tried to do bigger metadata blocks, but the
+ * code was pretty buggy. Lets not let them try anymore.
+ */
+#define BTRFS_FEATURE_INCOMPAT_BIG_METADATA (1ULL << 5)
+
+#define BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF (1ULL << 6)
+#define BTRFS_FEATURE_INCOMPAT_RAID56 (1ULL << 7)
+#define BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA (1ULL << 8)
+#define BTRFS_FEATURE_INCOMPAT_NO_HOLES (1ULL << 9)
+#define BTRFS_FEATURE_INCOMPAT_METADATA_UUID (1ULL << 10)
+#define BTRFS_FEATURE_INCOMPAT_RAID1C34 (1ULL << 11)
+
+/*
+ * Compat flags that we support.
+ *
+ * If any incompat flags are set other than the ones specified below then we
+ * will fail to mount.
+ */
+#define BTRFS_FEATURE_COMPAT_SUPP 0ULL
+#define BTRFS_FEATURE_COMPAT_SAFE_SET 0ULL
+#define BTRFS_FEATURE_COMPAT_SAFE_CLEAR 0ULL
+
+#define BTRFS_FEATURE_COMPAT_RO_SUPP \
+ (BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE | \
+ BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE_VALID)
+
+#define BTRFS_FEATURE_COMPAT_RO_SAFE_SET 0ULL
+#define BTRFS_FEATURE_COMPAT_RO_SAFE_CLEAR 0ULL
+
+#define BTRFS_FEATURE_INCOMPAT_SUPP \
+ (BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF | \
+ BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL | \
+ BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS | \
+ BTRFS_FEATURE_INCOMPAT_BIG_METADATA | \
+ BTRFS_FEATURE_INCOMPAT_COMPRESS_LZO | \
+ BTRFS_FEATURE_INCOMPAT_COMPRESS_ZSTD | \
+ BTRFS_FEATURE_INCOMPAT_RAID56 | \
+ BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF | \
+ BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA | \
+ BTRFS_FEATURE_INCOMPAT_NO_HOLES | \
+ BTRFS_FEATURE_INCOMPAT_METADATA_UUID | \
+ BTRFS_FEATURE_INCOMPAT_RAID1C34)
+
+#define BTRFS_FEATURE_INCOMPAT_SAFE_SET \
+ (BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF)
+#define BTRFS_FEATURE_INCOMPAT_SAFE_CLEAR 0ULL
+
+#define BTRFS_BACKREF_REV_MAX 256
+#define BTRFS_BACKREF_REV_SHIFT 56
+#define BTRFS_BACKREF_REV_MASK (((u64)BTRFS_BACKREF_REV_MAX - 1) << \
+ BTRFS_BACKREF_REV_SHIFT)
+
+#define BTRFS_OLD_BACKREF_REV 0
+#define BTRFS_MIXED_BACKREF_REV 1
+
+#define BTRFS_MAX_LEVEL 8
+
+/* Every tree block (leaf or node) starts with this header. */
+struct btrfs_header {
+ /* These first four must match the super block */
+ u8 csum[BTRFS_CSUM_SIZE];
+ u8 fsid[BTRFS_FSID_SIZE]; /* FS specific uuid */
+ __le64 bytenr; /* Which block this node is supposed to live in */
+ __le64 flags;
+
+ /* Allowed to be different from the super from here on down. */
+ u8 chunk_tree_uuid[BTRFS_UUID_SIZE];
+ __le64 generation;
+ __le64 owner;
+ __le32 nritems;
+ u8 level;
+} __attribute__ ((__packed__));
+
+/*
+ * A leaf is full of items. Offset and size tell us where to find
+ * the item in the leaf (relative to the start of the data area).
+ */
+struct btrfs_item {
+ struct btrfs_disk_key key;
+ __le32 offset;
+ __le32 size;
+} __attribute__ ((__packed__));
+
+/*
+ * leaves have an item area and a data area:
+ * [item0, item1....itemN] [free space] [dataN...data1, data0]
+ *
+ * The data is separate from the items to get the keys closer together
+ * during searches.
+ */
+struct btrfs_leaf {
+ struct btrfs_header header;
+ struct btrfs_item items[];
+} __attribute__ ((__packed__));
+
+/*
+ * All non-leaf blocks are nodes, they hold only keys and pointers to children
+ * blocks.
+ */
+struct btrfs_key_ptr {
+ struct btrfs_disk_key key;
+ __le64 blockptr;
+ __le64 generation;
+} __attribute__ ((__packed__));
+
+struct btrfs_node {
+ struct btrfs_header header;
+ struct btrfs_key_ptr ptrs[];
+} __attribute__ ((__packed__));
+
+#endif /* __BTRFS_TREE_H__ */
diff --git a/roms/u-boot/fs/btrfs/root-tree.c b/roms/u-boot/fs/btrfs/root-tree.c
new file mode 100644
index 000000000..a39ad7206
--- /dev/null
+++ b/roms/u-boot/fs/btrfs/root-tree.c
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include "ctree.h"
+
+int btrfs_find_last_root(struct btrfs_root *root, u64 objectid,
+ struct btrfs_root_item *item, struct btrfs_key *key)
+{
+ struct btrfs_path *path;
+ struct btrfs_key search_key;
+ struct btrfs_key found_key;
+ struct extent_buffer *l;
+ int ret;
+ int slot;
+
+ path = btrfs_alloc_path();
+ if (!path)
+ return -ENOMEM;
+
+ search_key.objectid = objectid;
+ search_key.type = BTRFS_ROOT_ITEM_KEY;
+ search_key.offset = (u64)-1;
+
+ ret = btrfs_search_slot(NULL, root, &search_key, path, 0, 0);
+ if (ret < 0)
+ goto out;
+ if (path->slots[0] == 0) {
+ ret = -ENOENT;
+ goto out;
+ }
+
+ BUG_ON(ret == 0);
+ l = path->nodes[0];
+ slot = path->slots[0] - 1;
+ btrfs_item_key_to_cpu(l, &found_key, slot);
+ if (found_key.type != BTRFS_ROOT_ITEM_KEY ||
+ found_key.objectid != objectid) {
+ ret = -ENOENT;
+ goto out;
+ }
+ read_extent_buffer(l, item, btrfs_item_ptr_offset(l, slot),
+ sizeof(*item));
+ memcpy(key, &found_key, sizeof(found_key));
+ ret = 0;
+out:
+ btrfs_free_path(path);
+ return ret;
+}
diff --git a/roms/u-boot/fs/btrfs/subvolume.c b/roms/u-boot/fs/btrfs/subvolume.c
new file mode 100644
index 000000000..2815673bc
--- /dev/null
+++ b/roms/u-boot/fs/btrfs/subvolume.c
@@ -0,0 +1,234 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * BTRFS filesystem implementation for U-Boot
+ *
+ * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz
+ */
+
+#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);
+}
diff --git a/roms/u-boot/fs/btrfs/volumes.c b/roms/u-boot/fs/btrfs/volumes.c
new file mode 100644
index 000000000..4aaaeab66
--- /dev/null
+++ b/roms/u-boot/fs/btrfs/volumes.c
@@ -0,0 +1,1173 @@
+// SPDX-License-Identifier: GPL-2.0+
+#include <stdlib.h>
+#include <common.h>
+#include <fs_internal.h>
+#include "ctree.h"
+#include "disk-io.h"
+#include "volumes.h"
+#include "extent-io.h"
+
+const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES] = {
+ [BTRFS_RAID_RAID10] = {
+ .sub_stripes = 2,
+ .dev_stripes = 1,
+ .devs_max = 0, /* 0 == as many as possible */
+ .devs_min = 4,
+ .tolerated_failures = 1,
+ .devs_increment = 2,
+ .ncopies = 2,
+ .nparity = 0,
+ .raid_name = "raid10",
+ .bg_flag = BTRFS_BLOCK_GROUP_RAID10,
+ },
+ [BTRFS_RAID_RAID1] = {
+ .sub_stripes = 1,
+ .dev_stripes = 1,
+ .devs_max = 2,
+ .devs_min = 2,
+ .tolerated_failures = 1,
+ .devs_increment = 2,
+ .ncopies = 2,
+ .nparity = 0,
+ .raid_name = "raid1",
+ .bg_flag = BTRFS_BLOCK_GROUP_RAID1,
+ },
+ [BTRFS_RAID_RAID1C3] = {
+ .sub_stripes = 1,
+ .dev_stripes = 1,
+ .devs_max = 3,
+ .devs_min = 3,
+ .tolerated_failures = 2,
+ .devs_increment = 3,
+ .ncopies = 3,
+ .raid_name = "raid1c3",
+ .bg_flag = BTRFS_BLOCK_GROUP_RAID1C3,
+ },
+ [BTRFS_RAID_RAID1C4] = {
+ .sub_stripes = 1,
+ .dev_stripes = 1,
+ .devs_max = 4,
+ .devs_min = 4,
+ .tolerated_failures = 3,
+ .devs_increment = 4,
+ .ncopies = 4,
+ .raid_name = "raid1c4",
+ .bg_flag = BTRFS_BLOCK_GROUP_RAID1C4,
+ },
+ [BTRFS_RAID_DUP] = {
+ .sub_stripes = 1,
+ .dev_stripes = 2,
+ .devs_max = 1,
+ .devs_min = 1,
+ .tolerated_failures = 0,
+ .devs_increment = 1,
+ .ncopies = 2,
+ .nparity = 0,
+ .raid_name = "dup",
+ .bg_flag = BTRFS_BLOCK_GROUP_DUP,
+ },
+ [BTRFS_RAID_RAID0] = {
+ .sub_stripes = 1,
+ .dev_stripes = 1,
+ .devs_max = 0,
+ .devs_min = 2,
+ .tolerated_failures = 0,
+ .devs_increment = 1,
+ .ncopies = 1,
+ .nparity = 0,
+ .raid_name = "raid0",
+ .bg_flag = BTRFS_BLOCK_GROUP_RAID0,
+ },
+ [BTRFS_RAID_SINGLE] = {
+ .sub_stripes = 1,
+ .dev_stripes = 1,
+ .devs_max = 1,
+ .devs_min = 1,
+ .tolerated_failures = 0,
+ .devs_increment = 1,
+ .ncopies = 1,
+ .nparity = 0,
+ .raid_name = "single",
+ .bg_flag = 0,
+ },
+ [BTRFS_RAID_RAID5] = {
+ .sub_stripes = 1,
+ .dev_stripes = 1,
+ .devs_max = 0,
+ .devs_min = 2,
+ .tolerated_failures = 1,
+ .devs_increment = 1,
+ .ncopies = 1,
+ .nparity = 1,
+ .raid_name = "raid5",
+ .bg_flag = BTRFS_BLOCK_GROUP_RAID5,
+ },
+ [BTRFS_RAID_RAID6] = {
+ .sub_stripes = 1,
+ .dev_stripes = 1,
+ .devs_max = 0,
+ .devs_min = 3,
+ .tolerated_failures = 2,
+ .devs_increment = 1,
+ .ncopies = 1,
+ .nparity = 2,
+ .raid_name = "raid6",
+ .bg_flag = BTRFS_BLOCK_GROUP_RAID6,
+ },
+};
+
+struct stripe {
+ struct btrfs_device *dev;
+ u64 physical;
+};
+
+static inline int nr_parity_stripes(struct map_lookup *map)
+{
+ if (map->type & BTRFS_BLOCK_GROUP_RAID5)
+ return 1;
+ else if (map->type & BTRFS_BLOCK_GROUP_RAID6)
+ return 2;
+ else
+ return 0;
+}
+
+static inline int nr_data_stripes(struct map_lookup *map)
+{
+ return map->num_stripes - nr_parity_stripes(map);
+}
+
+#define is_parity_stripe(x) ( ((x) == BTRFS_RAID5_P_STRIPE) || ((x) == BTRFS_RAID6_Q_STRIPE) )
+
+static LIST_HEAD(fs_uuids);
+
+/*
+ * Find a device specified by @devid or @uuid in the list of @fs_devices, or
+ * return NULL.
+ *
+ * If devid and uuid are both specified, the match must be exact, otherwise
+ * only devid is used.
+ */
+static struct btrfs_device *find_device(struct btrfs_fs_devices *fs_devices,
+ u64 devid, u8 *uuid)
+{
+ struct list_head *head = &fs_devices->devices;
+ struct btrfs_device *dev;
+
+ list_for_each_entry(dev, head, dev_list) {
+ if (dev->devid == devid &&
+ (!uuid || !memcmp(dev->uuid, uuid, BTRFS_UUID_SIZE))) {
+ return dev;
+ }
+ }
+ return NULL;
+}
+
+static struct btrfs_fs_devices *find_fsid(u8 *fsid, u8 *metadata_uuid)
+{
+ struct btrfs_fs_devices *fs_devices;
+
+ list_for_each_entry(fs_devices, &fs_uuids, list) {
+ if (metadata_uuid && (memcmp(fsid, fs_devices->fsid,
+ BTRFS_FSID_SIZE) == 0) &&
+ (memcmp(metadata_uuid, fs_devices->metadata_uuid,
+ BTRFS_FSID_SIZE) == 0)) {
+ return fs_devices;
+ } else if (memcmp(fsid, fs_devices->fsid, BTRFS_FSID_SIZE) == 0){
+ return fs_devices;
+ }
+ }
+ return NULL;
+}
+
+static int device_list_add(struct btrfs_super_block *disk_super,
+ u64 devid, struct blk_desc *desc,
+ struct disk_partition *part,
+ struct btrfs_fs_devices **fs_devices_ret)
+{
+ struct btrfs_device *device;
+ struct btrfs_fs_devices *fs_devices;
+ u64 found_transid = btrfs_super_generation(disk_super);
+ bool metadata_uuid = (btrfs_super_incompat_flags(disk_super) &
+ BTRFS_FEATURE_INCOMPAT_METADATA_UUID);
+
+ if (metadata_uuid)
+ fs_devices = find_fsid(disk_super->fsid,
+ disk_super->metadata_uuid);
+ else
+ fs_devices = find_fsid(disk_super->fsid, NULL);
+
+ if (!fs_devices) {
+ fs_devices = kzalloc(sizeof(*fs_devices), GFP_NOFS);
+ if (!fs_devices)
+ return -ENOMEM;
+ INIT_LIST_HEAD(&fs_devices->devices);
+ list_add(&fs_devices->list, &fs_uuids);
+ memcpy(fs_devices->fsid, disk_super->fsid, BTRFS_FSID_SIZE);
+ if (metadata_uuid)
+ memcpy(fs_devices->metadata_uuid,
+ disk_super->metadata_uuid, BTRFS_FSID_SIZE);
+ else
+ memcpy(fs_devices->metadata_uuid, fs_devices->fsid,
+ BTRFS_FSID_SIZE);
+
+ fs_devices->latest_devid = devid;
+ fs_devices->latest_trans = found_transid;
+ fs_devices->lowest_devid = (u64)-1;
+ device = NULL;
+ } else {
+ device = find_device(fs_devices, devid,
+ disk_super->dev_item.uuid);
+ }
+ if (!device) {
+ device = kzalloc(sizeof(*device), GFP_NOFS);
+ if (!device) {
+ /* we can safely leave the fs_devices entry around */
+ return -ENOMEM;
+ }
+ device->devid = devid;
+ device->desc = desc;
+ device->part = part;
+ device->generation = found_transid;
+ memcpy(device->uuid, disk_super->dev_item.uuid,
+ BTRFS_UUID_SIZE);
+ device->total_devs = btrfs_super_num_devices(disk_super);
+ device->super_bytes_used = btrfs_super_bytes_used(disk_super);
+ device->total_bytes =
+ btrfs_stack_device_total_bytes(&disk_super->dev_item);
+ device->bytes_used =
+ btrfs_stack_device_bytes_used(&disk_super->dev_item);
+ list_add(&device->dev_list, &fs_devices->devices);
+ device->fs_devices = fs_devices;
+ } else if (!device->desc || !device->part) {
+ /*
+ * The existing device has newer generation, so this one could
+ * be a stale one, don't add it.
+ */
+ if (found_transid < device->generation) {
+ error(
+ "adding devid %llu gen %llu but found an existing device gen %llu",
+ device->devid, found_transid,
+ device->generation);
+ return -EEXIST;
+ } else {
+ device->desc = desc;
+ device->part = part;
+ }
+ }
+
+
+ if (found_transid > fs_devices->latest_trans) {
+ fs_devices->latest_devid = devid;
+ fs_devices->latest_trans = found_transid;
+ }
+ if (fs_devices->lowest_devid > devid) {
+ fs_devices->lowest_devid = devid;
+ }
+ *fs_devices_ret = fs_devices;
+ return 0;
+}
+
+int btrfs_close_devices(struct btrfs_fs_devices *fs_devices)
+{
+ struct btrfs_fs_devices *seed_devices;
+ struct btrfs_device *device;
+ int ret = 0;
+
+again:
+ if (!fs_devices)
+ return 0;
+ while (!list_empty(&fs_devices->devices)) {
+ device = list_entry(fs_devices->devices.next,
+ struct btrfs_device, dev_list);
+ list_del(&device->dev_list);
+ /* free the memory */
+ free(device);
+ }
+
+ seed_devices = fs_devices->seed;
+ fs_devices->seed = NULL;
+ if (seed_devices) {
+ struct btrfs_fs_devices *orig;
+
+ orig = fs_devices;
+ fs_devices = seed_devices;
+ list_del(&orig->list);
+ free(orig);
+ goto again;
+ } else {
+ list_del(&fs_devices->list);
+ free(fs_devices);
+ }
+
+ return ret;
+}
+
+void btrfs_close_all_devices(void)
+{
+ struct btrfs_fs_devices *fs_devices;
+
+ while (!list_empty(&fs_uuids)) {
+ fs_devices = list_entry(fs_uuids.next, struct btrfs_fs_devices,
+ list);
+ btrfs_close_devices(fs_devices);
+ }
+}
+
+int btrfs_open_devices(struct btrfs_fs_devices *fs_devices)
+{
+ struct btrfs_device *device;
+
+ list_for_each_entry(device, &fs_devices->devices, dev_list) {
+ if (!device->desc || !device->part) {
+ printf("no device found for devid %llu, skip it \n",
+ device->devid);
+ continue;
+ }
+ }
+ return 0;
+}
+
+int btrfs_scan_one_device(struct blk_desc *desc, struct disk_partition *part,
+ struct btrfs_fs_devices **fs_devices_ret,
+ u64 *total_devs)
+{
+ struct btrfs_super_block *disk_super;
+ char buf[BTRFS_SUPER_INFO_SIZE];
+ int ret;
+ u64 devid;
+
+ disk_super = (struct btrfs_super_block *)buf;
+ ret = btrfs_read_dev_super(desc, part, disk_super);
+ if (ret < 0)
+ return -EIO;
+ devid = btrfs_stack_device_id(&disk_super->dev_item);
+ if (btrfs_super_flags(disk_super) & BTRFS_SUPER_FLAG_METADUMP)
+ *total_devs = 1;
+ else
+ *total_devs = btrfs_super_num_devices(disk_super);
+
+ ret = device_list_add(disk_super, devid, desc, part, fs_devices_ret);
+
+ return ret;
+}
+
+struct btrfs_device *btrfs_find_device(struct btrfs_fs_info *fs_info, u64 devid,
+ u8 *uuid, u8 *fsid)
+{
+ struct btrfs_device *device;
+ struct btrfs_fs_devices *cur_devices;
+
+ cur_devices = fs_info->fs_devices;
+ while (cur_devices) {
+ if (!fsid ||
+ !memcmp(cur_devices->metadata_uuid, fsid, BTRFS_FSID_SIZE)) {
+ device = find_device(cur_devices, devid, uuid);
+ if (device)
+ return device;
+ }
+ cur_devices = cur_devices->seed;
+ }
+ return NULL;
+}
+
+static struct btrfs_device *fill_missing_device(u64 devid)
+{
+ struct btrfs_device *device;
+
+ device = kzalloc(sizeof(*device), GFP_NOFS);
+ return device;
+}
+
+/*
+ * slot == -1: SYSTEM chunk
+ * return -EIO on error, otherwise return 0
+ */
+int btrfs_check_chunk_valid(struct btrfs_fs_info *fs_info,
+ struct extent_buffer *leaf,
+ struct btrfs_chunk *chunk,
+ int slot, u64 logical)
+{
+ u64 length;
+ u64 stripe_len;
+ u16 num_stripes;
+ u16 sub_stripes;
+ u64 type;
+ u32 chunk_ondisk_size;
+ u32 sectorsize = fs_info->sectorsize;
+
+ /*
+ * Basic chunk item size check. Note that btrfs_chunk already contains
+ * one stripe, so no "==" check.
+ */
+ if (slot >= 0 &&
+ btrfs_item_size_nr(leaf, slot) < sizeof(struct btrfs_chunk)) {
+ error("invalid chunk item size, have %u expect [%zu, %zu)",
+ btrfs_item_size_nr(leaf, slot),
+ sizeof(struct btrfs_chunk),
+ BTRFS_LEAF_DATA_SIZE(fs_info));
+ return -EUCLEAN;
+ }
+ length = btrfs_chunk_length(leaf, chunk);
+ stripe_len = btrfs_chunk_stripe_len(leaf, chunk);
+ num_stripes = btrfs_chunk_num_stripes(leaf, chunk);
+ sub_stripes = btrfs_chunk_sub_stripes(leaf, chunk);
+ type = btrfs_chunk_type(leaf, chunk);
+
+ if (num_stripes == 0) {
+ error("invalid num_stripes, have %u expect non-zero",
+ num_stripes);
+ return -EUCLEAN;
+ }
+ if (slot >= 0 && btrfs_chunk_item_size(num_stripes) !=
+ btrfs_item_size_nr(leaf, slot)) {
+ error("invalid chunk item size, have %u expect %lu",
+ btrfs_item_size_nr(leaf, slot),
+ btrfs_chunk_item_size(num_stripes));
+ return -EUCLEAN;
+ }
+
+ /*
+ * These valid checks may be insufficient to cover every corner cases.
+ */
+ if (!IS_ALIGNED(logical, sectorsize)) {
+ error("invalid chunk logical %llu", logical);
+ return -EIO;
+ }
+ if (btrfs_chunk_sector_size(leaf, chunk) != sectorsize) {
+ error("invalid chunk sectorsize %llu",
+ (unsigned long long)btrfs_chunk_sector_size(leaf, chunk));
+ return -EIO;
+ }
+ if (!length || !IS_ALIGNED(length, sectorsize)) {
+ error("invalid chunk length %llu", length);
+ return -EIO;
+ }
+ if (stripe_len != BTRFS_STRIPE_LEN) {
+ error("invalid chunk stripe length: %llu", stripe_len);
+ return -EIO;
+ }
+ /* Check on chunk item type */
+ if (slot == -1 && (type & BTRFS_BLOCK_GROUP_SYSTEM) == 0) {
+ error("invalid chunk type %llu", type);
+ return -EIO;
+ }
+ if (type & ~(BTRFS_BLOCK_GROUP_TYPE_MASK |
+ BTRFS_BLOCK_GROUP_PROFILE_MASK)) {
+ error("unrecognized chunk type: %llu",
+ ~(BTRFS_BLOCK_GROUP_TYPE_MASK |
+ BTRFS_BLOCK_GROUP_PROFILE_MASK) & type);
+ return -EIO;
+ }
+ if (!(type & BTRFS_BLOCK_GROUP_TYPE_MASK)) {
+ error("missing chunk type flag: %llu", type);
+ return -EIO;
+ }
+ if (!(is_power_of_2(type & BTRFS_BLOCK_GROUP_PROFILE_MASK) ||
+ (type & BTRFS_BLOCK_GROUP_PROFILE_MASK) == 0)) {
+ error("conflicting chunk type detected: %llu", type);
+ return -EIO;
+ }
+ if ((type & BTRFS_BLOCK_GROUP_PROFILE_MASK) &&
+ !is_power_of_2(type & BTRFS_BLOCK_GROUP_PROFILE_MASK)) {
+ error("conflicting chunk profile detected: %llu", type);
+ return -EIO;
+ }
+
+ chunk_ondisk_size = btrfs_chunk_item_size(num_stripes);
+ /*
+ * Btrfs_chunk contains at least one stripe, and for sys_chunk
+ * it can't exceed the system chunk array size
+ * For normal chunk, it should match its chunk item size.
+ */
+ if (num_stripes < 1 ||
+ (slot == -1 && chunk_ondisk_size > BTRFS_SYSTEM_CHUNK_ARRAY_SIZE) ||
+ (slot >= 0 && chunk_ondisk_size > btrfs_item_size_nr(leaf, slot))) {
+ error("invalid num_stripes: %u", num_stripes);
+ return -EIO;
+ }
+ /*
+ * Device number check against profile
+ */
+ if ((type & BTRFS_BLOCK_GROUP_RAID10 && (sub_stripes != 2 ||
+ !IS_ALIGNED(num_stripes, sub_stripes))) ||
+ (type & BTRFS_BLOCK_GROUP_RAID1 && num_stripes < 1) ||
+ (type & BTRFS_BLOCK_GROUP_RAID1C3 && num_stripes < 3) ||
+ (type & BTRFS_BLOCK_GROUP_RAID1C4 && num_stripes < 4) ||
+ (type & BTRFS_BLOCK_GROUP_RAID5 && num_stripes < 2) ||
+ (type & BTRFS_BLOCK_GROUP_RAID6 && num_stripes < 3) ||
+ (type & BTRFS_BLOCK_GROUP_DUP && num_stripes > 2) ||
+ ((type & BTRFS_BLOCK_GROUP_PROFILE_MASK) == 0 &&
+ num_stripes != 1)) {
+ error("Invalid num_stripes:sub_stripes %u:%u for profile %llu",
+ num_stripes, sub_stripes,
+ type & BTRFS_BLOCK_GROUP_PROFILE_MASK);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/*
+ * Slot is used to verify the chunk item is valid
+ *
+ * For sys chunk in superblock, pass -1 to indicate sys chunk.
+ */
+static int read_one_chunk(struct btrfs_fs_info *fs_info, struct btrfs_key *key,
+ struct extent_buffer *leaf,
+ struct btrfs_chunk *chunk, int slot)
+{
+ struct btrfs_mapping_tree *map_tree = &fs_info->mapping_tree;
+ struct map_lookup *map;
+ struct cache_extent *ce;
+ u64 logical;
+ u64 length;
+ u64 devid;
+ u8 uuid[BTRFS_UUID_SIZE];
+ int num_stripes;
+ int ret;
+ int i;
+
+ logical = key->offset;
+ length = btrfs_chunk_length(leaf, chunk);
+ num_stripes = btrfs_chunk_num_stripes(leaf, chunk);
+ /* Validation check */
+ ret = btrfs_check_chunk_valid(fs_info, leaf, chunk, slot, logical);
+ if (ret) {
+ error("%s checksums match, but it has an invalid chunk, %s",
+ (slot == -1) ? "Superblock" : "Metadata",
+ (slot == -1) ? "try btrfsck --repair -s <superblock> ie, 0,1,2" : "");
+ return ret;
+ }
+
+ ce = search_cache_extent(&map_tree->cache_tree, logical);
+
+ /* already mapped? */
+ if (ce && ce->start <= logical && ce->start + ce->size > logical) {
+ return 0;
+ }
+
+ map = kmalloc(btrfs_map_lookup_size(num_stripes), GFP_NOFS);
+ if (!map)
+ return -ENOMEM;
+
+ map->ce.start = logical;
+ map->ce.size = length;
+ map->num_stripes = num_stripes;
+ map->io_width = btrfs_chunk_io_width(leaf, chunk);
+ map->io_align = btrfs_chunk_io_align(leaf, chunk);
+ map->sector_size = btrfs_chunk_sector_size(leaf, chunk);
+ map->stripe_len = btrfs_chunk_stripe_len(leaf, chunk);
+ map->type = btrfs_chunk_type(leaf, chunk);
+ map->sub_stripes = btrfs_chunk_sub_stripes(leaf, chunk);
+
+ for (i = 0; i < num_stripes; i++) {
+ map->stripes[i].physical =
+ btrfs_stripe_offset_nr(leaf, chunk, i);
+ devid = btrfs_stripe_devid_nr(leaf, chunk, i);
+ read_extent_buffer(leaf, uuid, (unsigned long)
+ btrfs_stripe_dev_uuid_nr(chunk, i),
+ BTRFS_UUID_SIZE);
+ map->stripes[i].dev = btrfs_find_device(fs_info, devid, uuid,
+ NULL);
+ if (!map->stripes[i].dev) {
+ map->stripes[i].dev = fill_missing_device(devid);
+ printf("warning, device %llu is missing\n",
+ (unsigned long long)devid);
+ list_add(&map->stripes[i].dev->dev_list,
+ &fs_info->fs_devices->devices);
+ }
+
+ }
+ ret = insert_cache_extent(&map_tree->cache_tree, &map->ce);
+ if (ret < 0) {
+ errno = -ret;
+ error("failed to add chunk map start=%llu len=%llu: %d (%m)",
+ map->ce.start, map->ce.size, ret);
+ }
+
+ return ret;
+}
+
+static int fill_device_from_item(struct extent_buffer *leaf,
+ struct btrfs_dev_item *dev_item,
+ struct btrfs_device *device)
+{
+ unsigned long ptr;
+
+ device->devid = btrfs_device_id(leaf, dev_item);
+ device->total_bytes = btrfs_device_total_bytes(leaf, dev_item);
+ device->bytes_used = btrfs_device_bytes_used(leaf, dev_item);
+ device->type = btrfs_device_type(leaf, dev_item);
+ device->io_align = btrfs_device_io_align(leaf, dev_item);
+ device->io_width = btrfs_device_io_width(leaf, dev_item);
+ device->sector_size = btrfs_device_sector_size(leaf, dev_item);
+
+ ptr = (unsigned long)btrfs_device_uuid(dev_item);
+ read_extent_buffer(leaf, device->uuid, ptr, BTRFS_UUID_SIZE);
+
+ return 0;
+}
+
+static int read_one_dev(struct btrfs_fs_info *fs_info,
+ struct extent_buffer *leaf,
+ struct btrfs_dev_item *dev_item)
+{
+ struct btrfs_device *device;
+ u64 devid;
+ int ret = 0;
+ u8 fs_uuid[BTRFS_UUID_SIZE];
+ u8 dev_uuid[BTRFS_UUID_SIZE];
+
+ devid = btrfs_device_id(leaf, dev_item);
+ read_extent_buffer(leaf, dev_uuid,
+ (unsigned long)btrfs_device_uuid(dev_item),
+ BTRFS_UUID_SIZE);
+ read_extent_buffer(leaf, fs_uuid,
+ (unsigned long)btrfs_device_fsid(dev_item),
+ BTRFS_FSID_SIZE);
+
+ if (memcmp(fs_uuid, fs_info->fs_devices->fsid, BTRFS_UUID_SIZE)) {
+ error("Seed device is not yet supported\n");
+ return -ENOTSUPP;
+ }
+
+ device = btrfs_find_device(fs_info, devid, dev_uuid, fs_uuid);
+ if (!device) {
+ device = kzalloc(sizeof(*device), GFP_NOFS);
+ if (!device)
+ return -ENOMEM;
+ list_add(&device->dev_list,
+ &fs_info->fs_devices->devices);
+ }
+
+ fill_device_from_item(leaf, dev_item, device);
+ fs_info->fs_devices->total_rw_bytes +=
+ btrfs_device_total_bytes(leaf, dev_item);
+ return ret;
+}
+
+int btrfs_read_sys_array(struct btrfs_fs_info *fs_info)
+{
+ struct btrfs_super_block *super_copy = fs_info->super_copy;
+ struct extent_buffer *sb;
+ struct btrfs_disk_key *disk_key;
+ struct btrfs_chunk *chunk;
+ u8 *array_ptr;
+ unsigned long sb_array_offset;
+ int ret = 0;
+ u32 num_stripes;
+ u32 array_size;
+ u32 len = 0;
+ u32 cur_offset;
+ struct btrfs_key key;
+
+ if (fs_info->nodesize < BTRFS_SUPER_INFO_SIZE) {
+ printf("ERROR: nodesize %u too small to read superblock\n",
+ fs_info->nodesize);
+ return -EINVAL;
+ }
+ sb = alloc_dummy_extent_buffer(fs_info, BTRFS_SUPER_INFO_OFFSET,
+ BTRFS_SUPER_INFO_SIZE);
+ if (!sb)
+ return -ENOMEM;
+ btrfs_set_buffer_uptodate(sb);
+ write_extent_buffer(sb, super_copy, 0, sizeof(*super_copy));
+ array_size = btrfs_super_sys_array_size(super_copy);
+
+ array_ptr = super_copy->sys_chunk_array;
+ sb_array_offset = offsetof(struct btrfs_super_block, sys_chunk_array);
+ cur_offset = 0;
+
+ while (cur_offset < array_size) {
+ disk_key = (struct btrfs_disk_key *)array_ptr;
+ len = sizeof(*disk_key);
+ if (cur_offset + len > array_size)
+ goto out_short_read;
+
+ btrfs_disk_key_to_cpu(&key, disk_key);
+
+ array_ptr += len;
+ sb_array_offset += len;
+ cur_offset += len;
+
+ if (key.type == BTRFS_CHUNK_ITEM_KEY) {
+ chunk = (struct btrfs_chunk *)sb_array_offset;
+ /*
+ * At least one btrfs_chunk with one stripe must be
+ * present, exact stripe count check comes afterwards
+ */
+ len = btrfs_chunk_item_size(1);
+ if (cur_offset + len > array_size)
+ goto out_short_read;
+
+ num_stripes = btrfs_chunk_num_stripes(sb, chunk);
+ if (!num_stripes) {
+ printk(
+ "ERROR: invalid number of stripes %u in sys_array at offset %u\n",
+ num_stripes, cur_offset);
+ ret = -EIO;
+ break;
+ }
+
+ len = btrfs_chunk_item_size(num_stripes);
+ if (cur_offset + len > array_size)
+ goto out_short_read;
+
+ ret = read_one_chunk(fs_info, &key, sb, chunk, -1);
+ if (ret)
+ break;
+ } else {
+ printk(
+ "ERROR: unexpected item type %u in sys_array at offset %u\n",
+ (u32)key.type, cur_offset);
+ ret = -EIO;
+ break;
+ }
+ array_ptr += len;
+ sb_array_offset += len;
+ cur_offset += len;
+ }
+ free_extent_buffer(sb);
+ return ret;
+
+out_short_read:
+ printk("ERROR: sys_array too short to read %u bytes at offset %u\n",
+ len, cur_offset);
+ free_extent_buffer(sb);
+ return -EIO;
+}
+
+int btrfs_read_chunk_tree(struct btrfs_fs_info *fs_info)
+{
+ struct btrfs_path *path;
+ struct extent_buffer *leaf;
+ struct btrfs_key key;
+ struct btrfs_key found_key;
+ struct btrfs_root *root = fs_info->chunk_root;
+ int ret;
+ int slot;
+
+ path = btrfs_alloc_path();
+ if (!path)
+ return -ENOMEM;
+
+ /*
+ * Read all device items, and then all the chunk items. All
+ * device items are found before any chunk item (their object id
+ * is smaller than the lowest possible object id for a chunk
+ * item - BTRFS_FIRST_CHUNK_TREE_OBJECTID).
+ */
+ key.objectid = BTRFS_DEV_ITEMS_OBJECTID;
+ key.offset = 0;
+ key.type = 0;
+ ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+ if (ret < 0)
+ goto error;
+ while(1) {
+ leaf = path->nodes[0];
+ slot = path->slots[0];
+ if (slot >= btrfs_header_nritems(leaf)) {
+ ret = btrfs_next_leaf(root, path);
+ if (ret == 0)
+ continue;
+ if (ret < 0)
+ goto error;
+ break;
+ }
+ btrfs_item_key_to_cpu(leaf, &found_key, slot);
+ if (found_key.type == BTRFS_DEV_ITEM_KEY) {
+ struct btrfs_dev_item *dev_item;
+ dev_item = btrfs_item_ptr(leaf, slot,
+ struct btrfs_dev_item);
+ ret = read_one_dev(fs_info, leaf, dev_item);
+ if (ret < 0)
+ goto error;
+ } else if (found_key.type == BTRFS_CHUNK_ITEM_KEY) {
+ struct btrfs_chunk *chunk;
+ chunk = btrfs_item_ptr(leaf, slot, struct btrfs_chunk);
+ ret = read_one_chunk(fs_info, &found_key, leaf, chunk,
+ slot);
+ if (ret < 0)
+ goto error;
+ }
+ path->slots[0]++;
+ }
+
+ ret = 0;
+error:
+ btrfs_free_path(path);
+ return ret;
+}
+
+/*
+ * Get stripe length from chunk item and its stripe items
+ *
+ * Caller should only call this function after validating the chunk item
+ * by using btrfs_check_chunk_valid().
+ */
+u64 btrfs_stripe_length(struct btrfs_fs_info *fs_info,
+ struct extent_buffer *leaf,
+ struct btrfs_chunk *chunk)
+{
+ u64 stripe_len;
+ u64 chunk_len;
+ u32 num_stripes = btrfs_chunk_num_stripes(leaf, chunk);
+ u64 profile = btrfs_chunk_type(leaf, chunk) &
+ BTRFS_BLOCK_GROUP_PROFILE_MASK;
+
+ chunk_len = btrfs_chunk_length(leaf, chunk);
+
+ switch (profile) {
+ case 0: /* Single profile */
+ case BTRFS_BLOCK_GROUP_RAID1:
+ case BTRFS_BLOCK_GROUP_RAID1C3:
+ case BTRFS_BLOCK_GROUP_RAID1C4:
+ case BTRFS_BLOCK_GROUP_DUP:
+ stripe_len = chunk_len;
+ break;
+ case BTRFS_BLOCK_GROUP_RAID0:
+ stripe_len = chunk_len / num_stripes;
+ break;
+ case BTRFS_BLOCK_GROUP_RAID5:
+ stripe_len = chunk_len / (num_stripes - 1);
+ break;
+ case BTRFS_BLOCK_GROUP_RAID6:
+ stripe_len = chunk_len / (num_stripes - 2);
+ break;
+ case BTRFS_BLOCK_GROUP_RAID10:
+ stripe_len = chunk_len / (num_stripes /
+ btrfs_chunk_sub_stripes(leaf, chunk));
+ break;
+ default:
+ /* Invalid chunk profile found */
+ BUG_ON(1);
+ }
+ return stripe_len;
+}
+
+int btrfs_num_copies(struct btrfs_fs_info *fs_info, u64 logical, u64 len)
+{
+ struct btrfs_mapping_tree *map_tree = &fs_info->mapping_tree;
+ struct cache_extent *ce;
+ struct map_lookup *map;
+ int ret;
+
+ ce = search_cache_extent(&map_tree->cache_tree, logical);
+ if (!ce) {
+ fprintf(stderr, "No mapping for %llu-%llu\n",
+ (unsigned long long)logical,
+ (unsigned long long)logical+len);
+ return 1;
+ }
+ if (ce->start > logical || ce->start + ce->size < logical) {
+ fprintf(stderr, "Invalid mapping for %llu-%llu, got "
+ "%llu-%llu\n", (unsigned long long)logical,
+ (unsigned long long)logical+len,
+ (unsigned long long)ce->start,
+ (unsigned long long)ce->start + ce->size);
+ return 1;
+ }
+ map = container_of(ce, struct map_lookup, ce);
+
+ if (map->type & (BTRFS_BLOCK_GROUP_DUP | BTRFS_BLOCK_GROUP_RAID1 |
+ BTRFS_BLOCK_GROUP_RAID1C3 | BTRFS_BLOCK_GROUP_RAID1C4))
+ ret = map->num_stripes;
+ else if (map->type & BTRFS_BLOCK_GROUP_RAID10)
+ ret = map->sub_stripes;
+ else if (map->type & BTRFS_BLOCK_GROUP_RAID5)
+ ret = 2;
+ else if (map->type & BTRFS_BLOCK_GROUP_RAID6)
+ ret = 3;
+ else
+ ret = 1;
+ return ret;
+}
+
+int btrfs_next_bg(struct btrfs_fs_info *fs_info, u64 *logical,
+ u64 *size, u64 type)
+{
+ struct btrfs_mapping_tree *map_tree = &fs_info->mapping_tree;
+ struct cache_extent *ce;
+ struct map_lookup *map;
+ u64 cur = *logical;
+
+ ce = search_cache_extent(&map_tree->cache_tree, cur);
+
+ while (ce) {
+ /*
+ * only jump to next bg if our cur is not 0
+ * As the initial logical for btrfs_next_bg() is 0, and
+ * if we jump to next bg, we skipped a valid bg.
+ */
+ if (cur) {
+ ce = next_cache_extent(ce);
+ if (!ce)
+ return -ENOENT;
+ }
+
+ cur = ce->start;
+ map = container_of(ce, struct map_lookup, ce);
+ if (map->type & type) {
+ *logical = ce->start;
+ *size = ce->size;
+ return 0;
+ }
+ if (!cur)
+ ce = next_cache_extent(ce);
+ }
+
+ return -ENOENT;
+}
+
+static inline int parity_smaller(u64 a, u64 b)
+{
+ return a > b;
+}
+
+/* Bubble-sort the stripe set to put the parity/syndrome stripes last */
+static void sort_parity_stripes(struct btrfs_multi_bio *bbio, u64 *raid_map)
+{
+ struct btrfs_bio_stripe s;
+ int i;
+ u64 l;
+ int again = 1;
+
+ while (again) {
+ again = 0;
+ for (i = 0; i < bbio->num_stripes - 1; i++) {
+ if (parity_smaller(raid_map[i], raid_map[i+1])) {
+ s = bbio->stripes[i];
+ l = raid_map[i];
+ bbio->stripes[i] = bbio->stripes[i+1];
+ raid_map[i] = raid_map[i+1];
+ bbio->stripes[i+1] = s;
+ raid_map[i+1] = l;
+ again = 1;
+ }
+ }
+ }
+}
+
+int __btrfs_map_block(struct btrfs_fs_info *fs_info, int rw,
+ u64 logical, u64 *length, u64 *type,
+ struct btrfs_multi_bio **multi_ret, int mirror_num,
+ u64 **raid_map_ret)
+{
+ struct btrfs_mapping_tree *map_tree = &fs_info->mapping_tree;
+ struct cache_extent *ce;
+ struct map_lookup *map;
+ u64 offset;
+ u64 stripe_offset;
+ u64 *raid_map = NULL;
+ int stripe_nr;
+ int stripes_allocated = 8;
+ int stripes_required = 1;
+ int stripe_index;
+ int i;
+ struct btrfs_multi_bio *multi = NULL;
+
+ if (multi_ret && rw == READ) {
+ stripes_allocated = 1;
+ }
+again:
+ ce = search_cache_extent(&map_tree->cache_tree, logical);
+ if (!ce) {
+ kfree(multi);
+ *length = (u64)-1;
+ return -ENOENT;
+ }
+ if (ce->start > logical) {
+ kfree(multi);
+ *length = ce->start - logical;
+ return -ENOENT;
+ }
+
+ if (multi_ret) {
+ multi = kzalloc(btrfs_multi_bio_size(stripes_allocated),
+ GFP_NOFS);
+ if (!multi)
+ return -ENOMEM;
+ }
+ map = container_of(ce, struct map_lookup, ce);
+ offset = logical - ce->start;
+
+ if (rw == WRITE) {
+ if (map->type & (BTRFS_BLOCK_GROUP_RAID1 |
+ BTRFS_BLOCK_GROUP_RAID1C3 |
+ BTRFS_BLOCK_GROUP_RAID1C4 |
+ BTRFS_BLOCK_GROUP_DUP)) {
+ stripes_required = map->num_stripes;
+ } else if (map->type & BTRFS_BLOCK_GROUP_RAID10) {
+ stripes_required = map->sub_stripes;
+ }
+ }
+ if (map->type & (BTRFS_BLOCK_GROUP_RAID5 | BTRFS_BLOCK_GROUP_RAID6)
+ && multi_ret && ((rw & WRITE) || mirror_num > 1) && raid_map_ret) {
+ /* RAID[56] write or recovery. Return all stripes */
+ stripes_required = map->num_stripes;
+
+ /* Only allocate the map if we've already got a large enough multi_ret */
+ if (stripes_allocated >= stripes_required) {
+ raid_map = kmalloc(sizeof(u64) * map->num_stripes, GFP_NOFS);
+ if (!raid_map) {
+ kfree(multi);
+ return -ENOMEM;
+ }
+ }
+ }
+
+ /* if our multi bio struct is too small, back off and try again */
+ if (multi_ret && stripes_allocated < stripes_required) {
+ stripes_allocated = stripes_required;
+ kfree(multi);
+ multi = NULL;
+ goto again;
+ }
+ stripe_nr = offset;
+ /*
+ * stripe_nr counts the total number of stripes we have to stride
+ * to get to this block
+ */
+ stripe_nr = stripe_nr / map->stripe_len;
+
+ stripe_offset = stripe_nr * (u64)map->stripe_len;
+ BUG_ON(offset < stripe_offset);
+
+ /* stripe_offset is the offset of this block in its stripe*/
+ stripe_offset = offset - stripe_offset;
+
+ if (map->type & (BTRFS_BLOCK_GROUP_RAID0 | BTRFS_BLOCK_GROUP_RAID1 |
+ BTRFS_BLOCK_GROUP_RAID1C3 | BTRFS_BLOCK_GROUP_RAID1C4 |
+ BTRFS_BLOCK_GROUP_RAID5 | BTRFS_BLOCK_GROUP_RAID6 |
+ BTRFS_BLOCK_GROUP_RAID10 |
+ BTRFS_BLOCK_GROUP_DUP)) {
+ /* we limit the length of each bio to what fits in a stripe */
+ *length = min_t(u64, ce->size - offset,
+ map->stripe_len - stripe_offset);
+ } else {
+ *length = ce->size - offset;
+ }
+
+ if (!multi_ret)
+ goto out;
+
+ multi->num_stripes = 1;
+ stripe_index = 0;
+ if (map->type & (BTRFS_BLOCK_GROUP_RAID1 |
+ BTRFS_BLOCK_GROUP_RAID1C3 |
+ BTRFS_BLOCK_GROUP_RAID1C4)) {
+ if (rw == WRITE)
+ multi->num_stripes = map->num_stripes;
+ else if (mirror_num)
+ stripe_index = mirror_num - 1;
+ else
+ stripe_index = stripe_nr % map->num_stripes;
+ } else if (map->type & BTRFS_BLOCK_GROUP_RAID10) {
+ int factor = map->num_stripes / map->sub_stripes;
+
+ stripe_index = stripe_nr % factor;
+ stripe_index *= map->sub_stripes;
+
+ if (rw == WRITE)
+ multi->num_stripes = map->sub_stripes;
+ else if (mirror_num)
+ stripe_index += mirror_num - 1;
+
+ stripe_nr = stripe_nr / factor;
+ } else if (map->type & BTRFS_BLOCK_GROUP_DUP) {
+ if (rw == WRITE)
+ multi->num_stripes = map->num_stripes;
+ else if (mirror_num)
+ stripe_index = mirror_num - 1;
+ } else if (map->type & (BTRFS_BLOCK_GROUP_RAID5 |
+ BTRFS_BLOCK_GROUP_RAID6)) {
+
+ if (raid_map) {
+ int rot;
+ u64 tmp;
+ u64 raid56_full_stripe_start;
+ u64 full_stripe_len = nr_data_stripes(map) * map->stripe_len;
+
+ /*
+ * align the start of our data stripe in the logical
+ * address space
+ */
+ raid56_full_stripe_start = offset / full_stripe_len;
+ raid56_full_stripe_start *= full_stripe_len;
+
+ /* get the data stripe number */
+ stripe_nr = raid56_full_stripe_start / map->stripe_len;
+ stripe_nr = stripe_nr / nr_data_stripes(map);
+
+ /* Work out the disk rotation on this stripe-set */
+ rot = stripe_nr % map->num_stripes;
+
+ /* Fill in the logical address of each stripe */
+ tmp = (u64)stripe_nr * nr_data_stripes(map);
+
+ for (i = 0; i < nr_data_stripes(map); i++)
+ raid_map[(i+rot) % map->num_stripes] =
+ ce->start + (tmp + i) * map->stripe_len;
+
+ raid_map[(i+rot) % map->num_stripes] = BTRFS_RAID5_P_STRIPE;
+ if (map->type & BTRFS_BLOCK_GROUP_RAID6)
+ raid_map[(i+rot+1) % map->num_stripes] = BTRFS_RAID6_Q_STRIPE;
+
+ *length = map->stripe_len;
+ stripe_index = 0;
+ stripe_offset = 0;
+ multi->num_stripes = map->num_stripes;
+ } else {
+ stripe_index = stripe_nr % nr_data_stripes(map);
+ stripe_nr = stripe_nr / nr_data_stripes(map);
+
+ /*
+ * Mirror #0 or #1 means the original data block.
+ * Mirror #2 is RAID5 parity block.
+ * Mirror #3 is RAID6 Q block.
+ */
+ if (mirror_num > 1)
+ stripe_index = nr_data_stripes(map) + mirror_num - 2;
+
+ /* We distribute the parity blocks across stripes */
+ stripe_index = (stripe_nr + stripe_index) % map->num_stripes;
+ }
+ } else {
+ /*
+ * after this do_div call, stripe_nr is the number of stripes
+ * on this device we have to walk to find the data, and
+ * stripe_index is the number of our device in the stripe array
+ */
+ stripe_index = stripe_nr % map->num_stripes;
+ stripe_nr = stripe_nr / map->num_stripes;
+ }
+ BUG_ON(stripe_index >= map->num_stripes);
+
+ for (i = 0; i < multi->num_stripes; i++) {
+ multi->stripes[i].physical =
+ map->stripes[stripe_index].physical + stripe_offset +
+ stripe_nr * map->stripe_len;
+ multi->stripes[i].dev = map->stripes[stripe_index].dev;
+ stripe_index++;
+ }
+ *multi_ret = multi;
+
+ if (type)
+ *type = map->type;
+
+ if (raid_map) {
+ sort_parity_stripes(multi, raid_map);
+ *raid_map_ret = raid_map;
+ }
+out:
+ return 0;
+}
+
+int btrfs_map_block(struct btrfs_fs_info *fs_info, int rw,
+ u64 logical, u64 *length,
+ struct btrfs_multi_bio **multi_ret, int mirror_num,
+ u64 **raid_map_ret)
+{
+ return __btrfs_map_block(fs_info, rw, logical, length, NULL,
+ multi_ret, mirror_num, raid_map_ret);
+}
diff --git a/roms/u-boot/fs/btrfs/volumes.h b/roms/u-boot/fs/btrfs/volumes.h
new file mode 100644
index 000000000..9d1a07ae7
--- /dev/null
+++ b/roms/u-boot/fs/btrfs/volumes.h
@@ -0,0 +1,204 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#ifndef __BTRFS_VOLUMES_H__
+#define __BTRFS_VOLUMES_H__
+
+#include <fs_internal.h>
+#include "ctree.h"
+
+#define BTRFS_STRIPE_LEN SZ_64K
+
+struct btrfs_device {
+ struct list_head dev_list;
+ struct btrfs_root *dev_root;
+ struct btrfs_fs_devices *fs_devices;
+
+ struct blk_desc *desc;
+ struct disk_partition *part;
+
+ u64 total_devs;
+ u64 super_bytes_used;
+
+ u64 generation;
+
+ /* the internal btrfs device id */
+ u64 devid;
+
+ /* size of the device */
+ u64 total_bytes;
+
+ /* bytes used */
+ u64 bytes_used;
+
+ /* optimal io alignment for this device */
+ u32 io_align;
+
+ /* optimal io width for this device */
+ u32 io_width;
+
+ /* minimal io size for this device */
+ u32 sector_size;
+
+ /* type and info about this device */
+ u64 type;
+
+ /* physical drive uuid (or lvm uuid) */
+ u8 uuid[BTRFS_UUID_SIZE];
+};
+
+struct btrfs_fs_devices {
+ u8 fsid[BTRFS_FSID_SIZE]; /* FS specific uuid */
+ u8 metadata_uuid[BTRFS_FSID_SIZE]; /* FS specific uuid */
+
+ u64 latest_devid;
+ u64 lowest_devid;
+ u64 latest_trans;
+
+ u64 total_rw_bytes;
+
+ struct list_head devices;
+ struct list_head list;
+
+ int seeding;
+ struct btrfs_fs_devices *seed;
+};
+
+struct btrfs_bio_stripe {
+ struct btrfs_device *dev;
+ u64 physical;
+};
+
+struct btrfs_multi_bio {
+ int error;
+ int num_stripes;
+ struct btrfs_bio_stripe stripes[];
+};
+
+struct map_lookup {
+ struct cache_extent ce;
+ u64 type;
+ int io_align;
+ int io_width;
+ int stripe_len;
+ int sector_size;
+ int num_stripes;
+ int sub_stripes;
+ struct btrfs_bio_stripe stripes[];
+};
+
+struct btrfs_raid_attr {
+ int sub_stripes; /* sub_stripes info for map */
+ int dev_stripes; /* stripes per dev */
+ int devs_max; /* max devs to use */
+ int devs_min; /* min devs needed */
+ int tolerated_failures; /* max tolerated fail devs */
+ int devs_increment; /* ndevs has to be a multiple of this */
+ int ncopies; /* how many copies to data has */
+ int nparity; /* number of stripes worth of bytes to store
+ * parity information */
+ const char raid_name[8]; /* name of the raid */
+ u64 bg_flag; /* block group flag of the raid */
+};
+
+extern const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES];
+
+static inline enum btrfs_raid_types btrfs_bg_flags_to_raid_index(u64 flags)
+{
+ if (flags & BTRFS_BLOCK_GROUP_RAID10)
+ return BTRFS_RAID_RAID10;
+ else if (flags & BTRFS_BLOCK_GROUP_RAID1)
+ return BTRFS_RAID_RAID1;
+ else if (flags & BTRFS_BLOCK_GROUP_RAID1C3)
+ return BTRFS_RAID_RAID1C3;
+ else if (flags & BTRFS_BLOCK_GROUP_RAID1C4)
+ return BTRFS_RAID_RAID1C4;
+ else if (flags & BTRFS_BLOCK_GROUP_DUP)
+ return BTRFS_RAID_DUP;
+ else if (flags & BTRFS_BLOCK_GROUP_RAID0)
+ return BTRFS_RAID_RAID0;
+ else if (flags & BTRFS_BLOCK_GROUP_RAID5)
+ return BTRFS_RAID_RAID5;
+ else if (flags & BTRFS_BLOCK_GROUP_RAID6)
+ return BTRFS_RAID_RAID6;
+
+ return BTRFS_RAID_SINGLE; /* BTRFS_BLOCK_GROUP_SINGLE */
+}
+
+#define btrfs_multi_bio_size(n) (sizeof(struct btrfs_multi_bio) + \
+ (sizeof(struct btrfs_bio_stripe) * (n)))
+#define btrfs_map_lookup_size(n) (sizeof(struct map_lookup) + \
+ (sizeof(struct btrfs_bio_stripe) * (n)))
+
+#define BTRFS_RAID5_P_STRIPE ((u64)-2)
+#define BTRFS_RAID6_Q_STRIPE ((u64)-1)
+
+static inline u64 calc_stripe_length(u64 type, u64 length, int num_stripes)
+{
+ u64 stripe_size;
+
+ if (type & BTRFS_BLOCK_GROUP_RAID0) {
+ stripe_size = length;
+ stripe_size /= num_stripes;
+ } else if (type & BTRFS_BLOCK_GROUP_RAID10) {
+ stripe_size = length * 2;
+ stripe_size /= num_stripes;
+ } else if (type & BTRFS_BLOCK_GROUP_RAID5) {
+ stripe_size = length;
+ stripe_size /= (num_stripes - 1);
+ } else if (type & BTRFS_BLOCK_GROUP_RAID6) {
+ stripe_size = length;
+ stripe_size /= (num_stripes - 2);
+ } else {
+ stripe_size = length;
+ }
+ return stripe_size;
+}
+
+#ifndef READ
+#define READ 0
+#define WRITE 1
+#define READA 2
+#endif
+
+int __btrfs_map_block(struct btrfs_fs_info *fs_info, int rw,
+ u64 logical, u64 *length, u64 *type,
+ struct btrfs_multi_bio **multi_ret, int mirror_num,
+ u64 **raid_map);
+int btrfs_map_block(struct btrfs_fs_info *fs_info, int rw,
+ u64 logical, u64 *length,
+ struct btrfs_multi_bio **multi_ret, int mirror_num,
+ u64 **raid_map_ret);
+int btrfs_next_bg(struct btrfs_fs_info *map_tree, u64 *logical,
+ u64 *size, u64 type);
+static inline int btrfs_next_bg_metadata(struct btrfs_fs_info *fs_info,
+ u64 *logical, u64 *size)
+{
+ return btrfs_next_bg(fs_info, logical, size,
+ BTRFS_BLOCK_GROUP_METADATA);
+}
+static inline int btrfs_next_bg_system(struct btrfs_fs_info *fs_info,
+ u64 *logical, u64 *size)
+{
+ return btrfs_next_bg(fs_info, logical, size,
+ BTRFS_BLOCK_GROUP_SYSTEM);
+}
+int btrfs_read_sys_array(struct btrfs_fs_info *fs_info);
+int btrfs_read_chunk_tree(struct btrfs_fs_info *fs_info);
+int btrfs_open_devices(struct btrfs_fs_devices *fs_devices);
+int btrfs_close_devices(struct btrfs_fs_devices *fs_devices);
+void btrfs_close_all_devices(void);
+int btrfs_num_copies(struct btrfs_fs_info *fs_info, u64 logical, u64 len);
+int btrfs_scan_one_device(struct blk_desc *desc, struct disk_partition *part,
+ struct btrfs_fs_devices **fs_devices_ret,
+ u64 *total_devs);
+struct list_head *btrfs_scanned_uuids(void);
+struct btrfs_device *btrfs_find_device(struct btrfs_fs_info *fs_info, u64 devid,
+ u8 *uuid, u8 *fsid);
+int btrfs_check_chunk_valid(struct btrfs_fs_info *fs_info,
+ struct extent_buffer *leaf,
+ struct btrfs_chunk *chunk,
+ int slot, u64 logical);
+u64 btrfs_stripe_length(struct btrfs_fs_info *fs_info,
+ struct extent_buffer *leaf,
+ struct btrfs_chunk *chunk);
+#endif
diff --git a/roms/u-boot/fs/cbfs/Kconfig b/roms/u-boot/fs/cbfs/Kconfig
new file mode 100644
index 000000000..03980d830
--- /dev/null
+++ b/roms/u-boot/fs/cbfs/Kconfig
@@ -0,0 +1,20 @@
+config FS_CBFS
+ bool "Enable CBFS (Coreboot Filesystem)"
+ help
+ Define this to enable support for reading from a Coreboot
+ filesystem. This is a ROM-based filesystem used for accessing files
+ on systems that use coreboot as the first boot-loader and then load
+ U-Boot to actually boot the Operating System. You can also enable
+ CMD_CBFS to get command-line access.
+
+config SPL_FS_CBFS
+ bool "Enable CBFS (Coreboot Filesystem) in SPL"
+ help
+ Define this to enable support for reading from a Coreboot
+ filesystem. This is a ROM-based filesystem used for accessing files
+ on systems that use coreboot as the first boot-loader and then load
+ U-Boot to actually boot the Operating System.
+
+ Note that most functions in the CBFS API do not work with SPL. Only
+ those which accept a cbfs_priv * can be used, since BSS is not
+ available.
diff --git a/roms/u-boot/fs/cbfs/Makefile b/roms/u-boot/fs/cbfs/Makefile
new file mode 100644
index 000000000..e8e9dfe47
--- /dev/null
+++ b/roms/u-boot/fs/cbfs/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+
+obj-y := cbfs.o
diff --git a/roms/u-boot/fs/cbfs/cbfs.c b/roms/u-boot/fs/cbfs/cbfs.c
new file mode 100644
index 000000000..415ea28b8
--- /dev/null
+++ b/roms/u-boot/fs/cbfs/cbfs.c
@@ -0,0 +1,521 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+ */
+
+#include <common.h>
+#include <cbfs.h>
+#include <log.h>
+#include <malloc.h>
+#include <asm/byteorder.h>
+
+/* Offset of master header from the start of a coreboot ROM */
+#define MASTER_HDR_OFFSET 0x38
+
+static const u32 good_magic = 0x4f524243;
+static const u8 good_file_magic[] = "LARCHIVE";
+
+/**
+ * struct cbfs_priv - Private data for this driver
+ *
+ * @initialised: true if this CBFS has been inited
+ * @start: Start position of CBFS in memory, typically memory-mapped SPI flash
+ * @header: Header read from the CBFS, byte-swapped so U-Boot can access it
+ * @file_cache: List of file headers read from CBFS
+ * @result: Success/error result
+ */
+struct cbfs_priv {
+ bool initialized;
+ void *start;
+ struct cbfs_header header;
+ struct cbfs_cachenode *file_cache;
+ enum cbfs_result result;
+};
+
+static struct cbfs_priv cbfs_s;
+
+const char *file_cbfs_error(void)
+{
+ switch (cbfs_s.result) {
+ case CBFS_SUCCESS:
+ return "Success";
+ case CBFS_NOT_INITIALIZED:
+ return "CBFS not initialized";
+ case CBFS_BAD_HEADER:
+ return "Bad CBFS header";
+ case CBFS_BAD_FILE:
+ return "Bad CBFS file";
+ case CBFS_FILE_NOT_FOUND:
+ return "File not found";
+ default:
+ return "Unknown";
+ }
+}
+
+enum cbfs_result cbfs_get_result(void)
+{
+ return cbfs_s.result;
+}
+
+/* Do endian conversion on the CBFS header structure. */
+static void swap_header(struct cbfs_header *dest, struct cbfs_header *src)
+{
+ dest->magic = be32_to_cpu(src->magic);
+ dest->version = be32_to_cpu(src->version);
+ dest->rom_size = be32_to_cpu(src->rom_size);
+ dest->boot_block_size = be32_to_cpu(src->boot_block_size);
+ dest->align = be32_to_cpu(src->align);
+ dest->offset = be32_to_cpu(src->offset);
+}
+
+/* Do endian conversion on a CBFS file header. */
+static void swap_file_header(struct cbfs_fileheader *dest,
+ const struct cbfs_fileheader *src)
+{
+ memcpy(&dest->magic, &src->magic, sizeof(dest->magic));
+ dest->len = be32_to_cpu(src->len);
+ dest->type = be32_to_cpu(src->type);
+ dest->attributes_offset = be32_to_cpu(src->attributes_offset);
+ dest->offset = be32_to_cpu(src->offset);
+}
+
+/**
+ * fill_node() - Fill a node struct with information from the CBFS
+ *
+ * @node: Node to fill
+ * @start: Pointer to the start of the CBFS file in memory
+ * @header: Pointer to the header information (in our enddianess)
+ * @return 0 if OK, -EBADF if the header is too small
+ */
+static int fill_node(struct cbfs_cachenode *node, void *start,
+ struct cbfs_fileheader *header)
+{
+ uint name_len;
+ uint offset;
+
+ /* Check the header is large enough */
+ if (header->offset < sizeof(struct cbfs_fileheader))
+ return -EBADF;
+
+ node->next = NULL;
+ node->type = header->type;
+ node->data = start + header->offset;
+ node->data_length = header->len;
+ name_len = header->offset - sizeof(struct cbfs_fileheader);
+ node->name = start + sizeof(struct cbfs_fileheader);
+ node->name_length = name_len;
+ node->attr_offset = header->attributes_offset;
+ node->comp_algo = CBFS_COMPRESS_NONE;
+ node->decomp_size = 0;
+
+ for (offset = node->attr_offset; offset < header->offset;) {
+ const struct cbfs_file_attribute *attr;
+ uint tag, len;
+
+ attr = start + offset;
+ tag = be32_to_cpu(attr->tag);
+ len = be32_to_cpu(attr->len);
+ if (tag == CBFS_FILE_ATTR_TAG_COMPRESSION) {
+ struct cbfs_file_attr_compression *comp;
+
+ comp = start + offset;
+ node->comp_algo = be32_to_cpu(comp->compression);
+ node->decomp_size =
+ be32_to_cpu(comp->decompressed_size);
+ }
+
+ offset += len;
+ }
+
+ return 0;
+}
+
+/*
+ * Given a starting position in memory, scan forward, bounded by a size, and
+ * find the next valid CBFS file. No memory is allocated by this function. The
+ * caller is responsible for allocating space for the new file structure.
+ *
+ * @param start The location in memory to start from.
+ * @param size The size of the memory region to search.
+ * @param align The alignment boundaries to check on.
+ * @param node A pointer to the file structure to load.
+ * @param used A pointer to the count of of bytes scanned through,
+ * including the file if one is found.
+ *
+ * @return 0 if a file is found, -ENOENT if one isn't, -EBADF if a bad header
+ * is found.
+ */
+static int file_cbfs_next_file(struct cbfs_priv *priv, void *start, int size,
+ int align, struct cbfs_cachenode *node,
+ int *used)
+{
+ struct cbfs_fileheader header;
+
+ *used = 0;
+
+ while (size >= align) {
+ const struct cbfs_fileheader *file_header = start;
+ int ret;
+
+ /* Check if there's a file here. */
+ if (memcmp(good_file_magic, &file_header->magic,
+ sizeof(file_header->magic))) {
+ *used += align;
+ size -= align;
+ start += align;
+ continue;
+ }
+
+ swap_file_header(&header, file_header);
+ ret = fill_node(node, start, &header);
+ if (ret) {
+ priv->result = CBFS_BAD_FILE;
+ return log_msg_ret("fill", ret);
+ }
+
+ *used += ALIGN(header.len, align);
+ return 0;
+ }
+
+ return -ENOENT;
+}
+
+/* Look through a CBFS instance and copy file metadata into regular memory. */
+static int file_cbfs_fill_cache(struct cbfs_priv *priv, int size, int align)
+{
+ struct cbfs_cachenode *cache_node;
+ struct cbfs_cachenode *node;
+ struct cbfs_cachenode **cache_tail = &priv->file_cache;
+ void *start;
+
+ /* Clear out old information. */
+ cache_node = priv->file_cache;
+ while (cache_node) {
+ struct cbfs_cachenode *old_node = cache_node;
+ cache_node = cache_node->next;
+ free(old_node);
+ }
+ priv->file_cache = NULL;
+
+ start = priv->start;
+ while (size >= align) {
+ int used;
+ int ret;
+
+ node = malloc(sizeof(struct cbfs_cachenode));
+ if (!node)
+ return -ENOMEM;
+ ret = file_cbfs_next_file(priv, start, size, align, node,
+ &used);
+
+ if (ret < 0) {
+ free(node);
+ if (ret == -ENOENT)
+ break;
+ return ret;
+ }
+ *cache_tail = node;
+ cache_tail = &node->next;
+
+ size -= used;
+ start += used;
+ }
+ priv->result = CBFS_SUCCESS;
+
+ return 0;
+}
+
+/**
+ * load_header() - Load the CBFS header
+ *
+ * Get the CBFS header out of the ROM and do endian conversion.
+ *
+ * @priv: Private data, which is inited by this function
+ * @addr: Address of CBFS header in memory-mapped SPI flash
+ * @return 0 if OK, -ENXIO if the header is bad
+ */
+static int load_header(struct cbfs_priv *priv, ulong addr)
+{
+ struct cbfs_header *header = &priv->header;
+ struct cbfs_header *header_in_rom;
+
+ memset(priv, '\0', sizeof(*priv));
+ header_in_rom = (struct cbfs_header *)addr;
+ swap_header(header, header_in_rom);
+
+ if (header->magic != good_magic || header->offset >
+ header->rom_size - header->boot_block_size) {
+ priv->result = CBFS_BAD_HEADER;
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+/**
+ * file_cbfs_load_header() - Get the CBFS header out of the ROM, given the end
+ *
+ * @priv: Private data, which is inited by this function
+ * @end_of_rom: Address of the last byte of the ROM (typically 0xffffffff)
+ * @return 0 if OK, -ENXIO if the header is bad
+ */
+static int file_cbfs_load_header(struct cbfs_priv *priv, ulong end_of_rom)
+{
+ int offset = *(u32 *)(end_of_rom - 3);
+ int ret;
+
+ ret = load_header(priv, end_of_rom + offset + 1);
+ if (ret)
+ return ret;
+ priv->start = (void *)(end_of_rom + 1 - priv->header.rom_size);
+
+ return 0;
+}
+
+/**
+ * cbfs_load_header_ptr() - Get the CBFS header out of the ROM, given the base
+ *
+ * @priv: Private data, which is inited by this function
+ * @base: Address of the first byte of the ROM (e.g. 0xff000000)
+ * @return 0 if OK, -ENXIO if the header is bad
+ */
+static int cbfs_load_header_ptr(struct cbfs_priv *priv, ulong base)
+{
+ int ret;
+
+ ret = load_header(priv, base + MASTER_HDR_OFFSET);
+ if (ret)
+ return ret;
+ priv->start = (void *)base;
+
+ return 0;
+}
+
+static int cbfs_init(struct cbfs_priv *priv, ulong end_of_rom)
+{
+ int ret;
+
+ ret = file_cbfs_load_header(priv, end_of_rom);
+ if (ret)
+ return ret;
+
+ ret = file_cbfs_fill_cache(priv, priv->header.rom_size,
+ priv->header.align);
+ if (ret)
+ return ret;
+ priv->initialized = true;
+
+ return 0;
+}
+
+int file_cbfs_init(ulong end_of_rom)
+{
+ return cbfs_init(&cbfs_s, end_of_rom);
+}
+
+int cbfs_init_mem(ulong base, ulong size, bool require_hdr,
+ struct cbfs_priv **privp)
+{
+ struct cbfs_priv priv_s, *priv = &priv_s;
+ int ret;
+
+ /*
+ * Use a local variable to start with until we know that the * CBFS is
+ * valid. Note that size is detected from the header, if present,
+ * meaning the parameter is ignored.
+ */
+ ret = cbfs_load_header_ptr(priv, base);
+ if (ret) {
+ if (require_hdr || size == CBFS_SIZE_UNKNOWN)
+ return ret;
+ memset(priv, '\0', sizeof(struct cbfs_priv));
+ priv->header.rom_size = size;
+ priv->header.align = CBFS_ALIGN_SIZE;
+ priv->start = (void *)base;
+ }
+
+ ret = file_cbfs_fill_cache(priv, priv->header.rom_size,
+ priv->header.align);
+ if (ret)
+ return log_msg_ret("fill", ret);
+
+ priv->initialized = true;
+ priv = malloc(sizeof(priv_s));
+ if (!priv)
+ return -ENOMEM;
+ memcpy(priv, &priv_s, sizeof(priv_s));
+ *privp = priv;
+
+ return 0;
+}
+
+const struct cbfs_header *file_cbfs_get_header(void)
+{
+ struct cbfs_priv *priv = &cbfs_s;
+
+ if (priv->initialized) {
+ priv->result = CBFS_SUCCESS;
+ return &priv->header;
+ } else {
+ priv->result = CBFS_NOT_INITIALIZED;
+ return NULL;
+ }
+}
+
+const struct cbfs_cachenode *cbfs_get_first(const struct cbfs_priv *priv)
+{
+ return priv->file_cache;
+}
+
+void cbfs_get_next(const struct cbfs_cachenode **filep)
+{
+ if (*filep)
+ *filep = (*filep)->next;
+}
+
+const struct cbfs_cachenode *file_cbfs_get_first(void)
+{
+ struct cbfs_priv *priv = &cbfs_s;
+
+ if (!priv->initialized) {
+ priv->result = CBFS_NOT_INITIALIZED;
+ return NULL;
+ } else {
+ priv->result = CBFS_SUCCESS;
+ return priv->file_cache;
+ }
+}
+
+void file_cbfs_get_next(const struct cbfs_cachenode **file)
+{
+ struct cbfs_priv *priv = &cbfs_s;
+
+ if (!priv->initialized) {
+ priv->result = CBFS_NOT_INITIALIZED;
+ *file = NULL;
+ return;
+ }
+
+ if (*file)
+ *file = (*file)->next;
+ priv->result = CBFS_SUCCESS;
+}
+
+const struct cbfs_cachenode *cbfs_find_file(struct cbfs_priv *priv,
+ const char *name)
+{
+ struct cbfs_cachenode *cache_node = priv->file_cache;
+
+ if (!priv->initialized) {
+ priv->result = CBFS_NOT_INITIALIZED;
+ return NULL;
+ }
+
+ while (cache_node) {
+ if (!strcmp(name, cache_node->name))
+ break;
+ cache_node = cache_node->next;
+ }
+ if (!cache_node)
+ priv->result = CBFS_FILE_NOT_FOUND;
+ else
+ priv->result = CBFS_SUCCESS;
+
+ return cache_node;
+}
+
+const struct cbfs_cachenode *file_cbfs_find(const char *name)
+{
+ return cbfs_find_file(&cbfs_s, name);
+}
+
+static int find_uncached(struct cbfs_priv *priv, const char *name, void *start,
+ struct cbfs_cachenode *node)
+{
+ int size = priv->header.rom_size;
+ int align = priv->header.align;
+
+ while (size >= align) {
+ int used;
+ int ret;
+
+ ret = file_cbfs_next_file(priv, start, size, align, node,
+ &used);
+ if (ret == -ENOENT)
+ break;
+ else if (ret)
+ return ret;
+ if (!strcmp(name, node->name))
+ return 0;
+
+ size -= used;
+ start += used;
+ }
+ priv->result = CBFS_FILE_NOT_FOUND;
+
+ return -ENOENT;
+}
+
+int file_cbfs_find_uncached(ulong end_of_rom, const char *name,
+ struct cbfs_cachenode *node)
+{
+ struct cbfs_priv priv;
+ void *start;
+ int ret;
+
+ ret = file_cbfs_load_header(&priv, end_of_rom);
+ if (ret)
+ return ret;
+ start = priv.start;
+
+ return find_uncached(&priv, name, start, node);
+}
+
+int file_cbfs_find_uncached_base(ulong base, const char *name,
+ struct cbfs_cachenode *node)
+{
+ struct cbfs_priv priv;
+ int ret;
+
+ ret = cbfs_load_header_ptr(&priv, base);
+ if (ret)
+ return ret;
+
+ return find_uncached(&priv, name, (void *)base, node);
+}
+
+const char *file_cbfs_name(const struct cbfs_cachenode *file)
+{
+ cbfs_s.result = CBFS_SUCCESS;
+
+ return file->name;
+}
+
+u32 file_cbfs_size(const struct cbfs_cachenode *file)
+{
+ cbfs_s.result = CBFS_SUCCESS;
+
+ return file->data_length;
+}
+
+u32 file_cbfs_type(const struct cbfs_cachenode *file)
+{
+ cbfs_s.result = CBFS_SUCCESS;
+
+ return file->type;
+}
+
+long file_cbfs_read(const struct cbfs_cachenode *file, void *buffer,
+ unsigned long maxsize)
+{
+ u32 size;
+
+ size = file->data_length;
+ if (maxsize && size > maxsize)
+ size = maxsize;
+
+ memcpy(buffer, file->data, size);
+ cbfs_s.result = CBFS_SUCCESS;
+
+ return size;
+}
diff --git a/roms/u-boot/fs/cramfs/Kconfig b/roms/u-boot/fs/cramfs/Kconfig
new file mode 100644
index 000000000..6c9f63d6f
--- /dev/null
+++ b/roms/u-boot/fs/cramfs/Kconfig
@@ -0,0 +1,7 @@
+config FS_CRAMFS
+ bool "Enable CRAMFS filesystem support"
+ help
+ This provides support for reading images from CRAMFS (Compressed ROM
+ filesystem). CRAMFS is useful when space is tight since files are
+ compressed. You can also enable CMD_CRAMFS to get command-line
+ access.
diff --git a/roms/u-boot/fs/cramfs/Makefile b/roms/u-boot/fs/cramfs/Makefile
new file mode 100644
index 000000000..2bf4187f8
--- /dev/null
+++ b/roms/u-boot/fs/cramfs/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright 2000-2006
+# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+
+obj-y := cramfs.o
+obj-y += uncompress.o
diff --git a/roms/u-boot/fs/cramfs/cramfs.c b/roms/u-boot/fs/cramfs/cramfs.c
new file mode 100644
index 000000000..7ef48bbc0
--- /dev/null
+++ b/roms/u-boot/fs/cramfs/cramfs.c
@@ -0,0 +1,387 @@
+/*
+ * cramfs.c
+ *
+ * Copyright (C) 1999 Linus Torvalds
+ *
+ * Copyright (C) 2000-2002 Transmeta Corporation
+ *
+ * Copyright (C) 2003 Kai-Uwe Bloem,
+ * Auerswald GmbH & Co KG, <linux-development@auerswald.de>
+ * - adapted from the www.tuxbox.org u-boot tree, added "ls" command
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * Compressed ROM filesystem for Linux.
+ *
+ * TODO:
+ * add support for resolving symbolic links
+ */
+
+/*
+ * These are the VFS interfaces to the compressed ROM filesystem.
+ * The actual compression is based on zlib, see the other files.
+ */
+
+#include <common.h>
+#include <flash.h>
+#include <malloc.h>
+#include <asm/byteorder.h>
+#include <linux/stat.h>
+#include <jffs2/jffs2.h>
+#include <jffs2/load_kernel.h>
+#include <cramfs/cramfs_fs.h>
+
+/* These two macros may change in future, to provide better st_ino
+ semantics. */
+#define CRAMINO(x) (CRAMFS_GET_OFFSET(x) ? CRAMFS_GET_OFFSET(x)<<2 : 1)
+#define OFFSET(x) ((x)->i_ino)
+
+struct cramfs_super super;
+
+/* CPU address space offset calculation macro, struct part_info offset is
+ * device address space offset, so we need to shift it by a device start address. */
+#if defined(CONFIG_MTD_NOR_FLASH)
+extern flash_info_t flash_info[];
+#define PART_OFFSET(x) ((ulong)x->offset + \
+ flash_info[x->dev->id->num].start[0])
+#else
+#define PART_OFFSET(x) ((ulong)x->offset)
+#endif
+
+static int cramfs_uncompress (unsigned long begin, unsigned long offset,
+ unsigned long loadoffset);
+
+static int cramfs_read_super (struct part_info *info)
+{
+ unsigned long root_offset;
+
+ /* Read the first block and get the superblock from it */
+ memcpy (&super, (void *) PART_OFFSET(info), sizeof (super));
+
+ /* Do sanity checks on the superblock */
+ if (super.magic != CRAMFS_32 (CRAMFS_MAGIC)) {
+ /* check at 512 byte offset */
+ memcpy (&super, (void *) PART_OFFSET(info) + 512, sizeof (super));
+ if (super.magic != CRAMFS_32 (CRAMFS_MAGIC)) {
+ printf ("cramfs: wrong magic\n");
+ return -1;
+ }
+ }
+
+ /* flags is reused several times, so swab it once */
+ super.flags = CRAMFS_32 (super.flags);
+ super.size = CRAMFS_32 (super.size);
+
+ /* get feature flags first */
+ if (super.flags & ~CRAMFS_SUPPORTED_FLAGS) {
+ printf ("cramfs: unsupported filesystem features\n");
+ return -1;
+ }
+
+ /* Check that the root inode is in a sane state */
+ if (!S_ISDIR (CRAMFS_16 (super.root.mode))) {
+ printf ("cramfs: root is not a directory\n");
+ return -1;
+ }
+ root_offset = CRAMFS_GET_OFFSET (&(super.root)) << 2;
+ if (root_offset == 0) {
+ printf ("cramfs: empty filesystem");
+ } else if (!(super.flags & CRAMFS_FLAG_SHIFTED_ROOT_OFFSET) &&
+ ((root_offset != sizeof (struct cramfs_super)) &&
+ (root_offset != 512 + sizeof (struct cramfs_super)))) {
+ printf ("cramfs: bad root offset %lu\n", root_offset);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Unpack to an allocated buffer, trusting in the inode's size field. */
+static char *cramfs_uncompress_link (unsigned long begin, unsigned long offset)
+{
+ struct cramfs_inode *inode = (struct cramfs_inode *)(begin + offset);
+ unsigned long size = CRAMFS_24 (inode->size);
+ char *link = malloc (size + 1);
+
+ if (!link || cramfs_uncompress (begin, offset, (unsigned long)link) != size) {
+ free (link);
+ link = NULL;
+ } else {
+ link[size] = '\0';
+ }
+ return link;
+}
+
+static unsigned long cramfs_resolve (unsigned long begin, unsigned long offset,
+ unsigned long size, int raw,
+ char *filename)
+{
+ unsigned long inodeoffset = 0, nextoffset;
+
+ while (inodeoffset < size) {
+ struct cramfs_inode *inode;
+ char *name;
+ int namelen;
+
+ inode = (struct cramfs_inode *) (begin + offset +
+ inodeoffset);
+
+ /*
+ * Namelengths on disk are shifted by two
+ * and the name padded out to 4-byte boundaries
+ * with zeroes.
+ */
+ namelen = CRAMFS_GET_NAMELEN (inode) << 2;
+ name = (char *) inode + sizeof (struct cramfs_inode);
+
+ nextoffset =
+ inodeoffset + sizeof (struct cramfs_inode) + namelen;
+
+ for (;;) {
+ if (!namelen)
+ return -1;
+ if (name[namelen - 1])
+ break;
+ namelen--;
+ }
+
+ if (!strncmp(filename, name, namelen) &&
+ (namelen == strlen(filename))) {
+ char *p = strtok (NULL, "/");
+
+ if (raw && (p == NULL || *p == '\0'))
+ return offset + inodeoffset;
+
+ if (S_ISDIR (CRAMFS_16 (inode->mode))) {
+ return cramfs_resolve (begin,
+ CRAMFS_GET_OFFSET
+ (inode) << 2,
+ CRAMFS_24 (inode->
+ size), raw,
+ p);
+ } else if (S_ISREG (CRAMFS_16 (inode->mode))) {
+ return offset + inodeoffset;
+ } else if (S_ISLNK (CRAMFS_16 (inode->mode))) {
+ unsigned long ret;
+ char *link;
+ if (p && strlen(p)) {
+ printf ("unsupported symlink to \
+ non-terminal path\n");
+ return 0;
+ }
+ link = cramfs_uncompress_link (begin,
+ offset + inodeoffset);
+ if (!link) {
+ printf ("%*.*s: Error reading link\n",
+ namelen, namelen, name);
+ return 0;
+ } else if (link[0] == '/') {
+ printf ("unsupported symlink to \
+ absolute path\n");
+ free (link);
+ return 0;
+ }
+ ret = cramfs_resolve (begin,
+ offset,
+ size,
+ raw,
+ strtok(link, "/"));
+ free (link);
+ return ret;
+ } else {
+ printf ("%*.*s: unsupported file type (%x)\n",
+ namelen, namelen, name,
+ CRAMFS_16 (inode->mode));
+ return 0;
+ }
+ }
+
+ inodeoffset = nextoffset;
+ }
+
+ printf ("can't find corresponding entry\n");
+ return 0;
+}
+
+static int cramfs_uncompress (unsigned long begin, unsigned long offset,
+ unsigned long loadoffset)
+{
+ struct cramfs_inode *inode = (struct cramfs_inode *) (begin + offset);
+ u32 *block_ptrs = (u32 *)
+ (begin + (CRAMFS_GET_OFFSET (inode) << 2));
+ unsigned long curr_block = (CRAMFS_GET_OFFSET (inode) +
+ (((CRAMFS_24 (inode->size)) +
+ 4095) >> 12)) << 2;
+ int size, total_size = 0;
+ int i;
+
+ cramfs_uncompress_init ();
+
+ for (i = 0; i < ((CRAMFS_24 (inode->size) + 4095) >> 12); i++) {
+ size = cramfs_uncompress_block ((void *) loadoffset,
+ (void *) (begin + curr_block),
+ (CRAMFS_32 (block_ptrs[i]) -
+ curr_block));
+ if (size < 0)
+ return size;
+ loadoffset += size;
+ total_size += size;
+ curr_block = CRAMFS_32 (block_ptrs[i]);
+ }
+
+ cramfs_uncompress_exit ();
+ return total_size;
+}
+
+int cramfs_load (char *loadoffset, struct part_info *info, char *filename)
+{
+ unsigned long offset;
+
+ if (cramfs_read_super (info))
+ return -1;
+
+ offset = cramfs_resolve (PART_OFFSET(info),
+ CRAMFS_GET_OFFSET (&(super.root)) << 2,
+ CRAMFS_24 (super.root.size), 0,
+ strtok (filename, "/"));
+
+ if (offset <= 0)
+ return offset;
+
+ return cramfs_uncompress (PART_OFFSET(info), offset,
+ (unsigned long) loadoffset);
+}
+
+static int cramfs_list_inode (struct part_info *info, unsigned long offset)
+{
+ struct cramfs_inode *inode = (struct cramfs_inode *)
+ (PART_OFFSET(info) + offset);
+ char *name, str[20];
+ int namelen, nextoff;
+
+ /*
+ * Namelengths on disk are shifted by two
+ * and the name padded out to 4-byte boundaries
+ * with zeroes.
+ */
+ namelen = CRAMFS_GET_NAMELEN (inode) << 2;
+ name = (char *) inode + sizeof (struct cramfs_inode);
+ nextoff = namelen;
+
+ for (;;) {
+ if (!namelen)
+ return namelen;
+ if (name[namelen - 1])
+ break;
+ namelen--;
+ }
+
+ printf (" %s %8d %*.*s", mkmodestr (CRAMFS_16 (inode->mode), str),
+ CRAMFS_24 (inode->size), namelen, namelen, name);
+
+ if ((CRAMFS_16 (inode->mode) & S_IFMT) == S_IFLNK) {
+ char *link = cramfs_uncompress_link (PART_OFFSET(info), offset);
+ if (link)
+ printf (" -> %s\n", link);
+ else
+ printf (" [Error reading link]\n");
+ free (link);
+ } else
+ printf ("\n");
+
+ return nextoff;
+}
+
+int cramfs_ls (struct part_info *info, char *filename)
+{
+ struct cramfs_inode *inode;
+ unsigned long inodeoffset = 0, nextoffset;
+ unsigned long offset, size;
+
+ if (cramfs_read_super (info))
+ return -1;
+
+ if (strlen (filename) == 0 || !strcmp (filename, "/")) {
+ /* Root directory. Use root inode in super block */
+ offset = CRAMFS_GET_OFFSET (&(super.root)) << 2;
+ size = CRAMFS_24 (super.root.size);
+ } else {
+ /* Resolve the path */
+ offset = cramfs_resolve (PART_OFFSET(info),
+ CRAMFS_GET_OFFSET (&(super.root)) <<
+ 2, CRAMFS_24 (super.root.size), 1,
+ strtok (filename, "/"));
+
+ if (offset <= 0)
+ return offset;
+
+ /* Resolving was successful. Examine the inode */
+ inode = (struct cramfs_inode *) (PART_OFFSET(info) + offset);
+ if (!S_ISDIR (CRAMFS_16 (inode->mode))) {
+ /* It's not a directory - list it, and that's that */
+ return (cramfs_list_inode (info, offset) > 0);
+ }
+
+ /* It's a directory. List files within */
+ offset = CRAMFS_GET_OFFSET (inode) << 2;
+ size = CRAMFS_24 (inode->size);
+ }
+
+ /* List the given directory */
+ while (inodeoffset < size) {
+ inode = (struct cramfs_inode *) (PART_OFFSET(info) + offset +
+ inodeoffset);
+
+ nextoffset = cramfs_list_inode (info, offset + inodeoffset);
+ if (nextoffset == 0)
+ break;
+ inodeoffset += sizeof (struct cramfs_inode) + nextoffset;
+ }
+
+ return 1;
+}
+
+int cramfs_info (struct part_info *info)
+{
+ if (cramfs_read_super (info))
+ return 0;
+
+ printf ("size: 0x%x (%u)\n", super.size, super.size);
+
+ if (super.flags != 0) {
+ printf ("flags:\n");
+ if (super.flags & CRAMFS_FLAG_FSID_VERSION_2)
+ printf ("\tFSID version 2\n");
+ if (super.flags & CRAMFS_FLAG_SORTED_DIRS)
+ printf ("\tsorted dirs\n");
+ if (super.flags & CRAMFS_FLAG_HOLES)
+ printf ("\tholes\n");
+ if (super.flags & CRAMFS_FLAG_SHIFTED_ROOT_OFFSET)
+ printf ("\tshifted root offset\n");
+ }
+
+ printf ("fsid:\n\tcrc: 0x%x\n\tedition: 0x%x\n",
+ super.fsid.crc, super.fsid.edition);
+ printf ("name: %16s\n", super.name);
+
+ return 1;
+}
+
+int cramfs_check (struct part_info *info)
+{
+ struct cramfs_super *sb;
+
+ if (info->dev->id->type != MTD_DEV_TYPE_NOR)
+ return 0;
+
+ sb = (struct cramfs_super *) PART_OFFSET(info);
+ if (sb->magic != CRAMFS_32 (CRAMFS_MAGIC)) {
+ /* check at 512 byte offset */
+ sb = (struct cramfs_super *) (PART_OFFSET(info) + 512);
+ if (sb->magic != CRAMFS_32 (CRAMFS_MAGIC))
+ return 0;
+ }
+ return 1;
+}
diff --git a/roms/u-boot/fs/cramfs/uncompress.c b/roms/u-boot/fs/cramfs/uncompress.c
new file mode 100644
index 000000000..f431cc46c
--- /dev/null
+++ b/roms/u-boot/fs/cramfs/uncompress.c
@@ -0,0 +1,83 @@
+/*
+ * uncompress.c
+ *
+ * Copyright (C) 1999 Linus Torvalds
+ * Copyright (C) 2000-2002 Transmeta Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * cramfs interfaces to the uncompression library. There's really just
+ * three entrypoints:
+ *
+ * - cramfs_uncompress_init() - called to initialize the thing.
+ * - cramfs_uncompress_exit() - tell me when you're done
+ * - cramfs_uncompress_block() - uncompress a block.
+ *
+ * NOTE NOTE NOTE! The uncompression is entirely single-threaded. We
+ * only have one stream, and we'll initialize it only once even if it
+ * then is used by multiple filesystems.
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <watchdog.h>
+#include <u-boot/zlib.h>
+
+static z_stream stream;
+
+/* Returns length of decompressed data. */
+int cramfs_uncompress_block (void *dst, void *src, int srclen)
+{
+ int err;
+
+ inflateReset (&stream);
+
+ stream.next_in = src;
+ stream.avail_in = srclen;
+
+ stream.next_out = dst;
+ stream.avail_out = 4096 * 2;
+
+ err = inflate (&stream, Z_FINISH);
+
+ if (err != Z_STREAM_END)
+ goto err;
+ return stream.total_out;
+
+ err:
+ /*printf ("Error %d while decompressing!\n", err); */
+ /*printf ("%p(%d)->%p\n", src, srclen, dst); */
+ return -1;
+}
+
+int cramfs_uncompress_init (void)
+{
+ int err;
+
+ stream.zalloc = gzalloc;
+ stream.zfree = gzfree;
+ stream.next_in = 0;
+ stream.avail_in = 0;
+
+#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)
+ stream.outcb = (cb_func) WATCHDOG_RESET;
+#else
+ stream.outcb = Z_NULL;
+#endif /* CONFIG_HW_WATCHDOG */
+
+ err = inflateInit (&stream);
+ if (err != Z_OK) {
+ printf ("Error: inflateInit2() returned %d\n", err);
+ return -1;
+ }
+
+ return 0;
+}
+
+int cramfs_uncompress_exit (void)
+{
+ inflateEnd (&stream);
+ return 0;
+}
diff --git a/roms/u-boot/fs/ext4/Kconfig b/roms/u-boot/fs/ext4/Kconfig
new file mode 100644
index 000000000..1a913d2b6
--- /dev/null
+++ b/roms/u-boot/fs/ext4/Kconfig
@@ -0,0 +1,13 @@
+config FS_EXT4
+ bool "Enable ext4 filesystem support"
+ help
+ This provides support for reading images from the ext4 filesystem.
+ ext4 is a widely used general-purpose filesystem for Linux.
+ You can also enable CMD_EXT4 to get access to ext4 commands.
+
+config EXT4_WRITE
+ bool "Enable ext4 filesystem write support"
+ depends on FS_EXT4
+ help
+ This provides support for creating and writing new files to an
+ existing ext4 filesystem partition.
diff --git a/roms/u-boot/fs/ext4/Makefile b/roms/u-boot/fs/ext4/Makefile
new file mode 100644
index 000000000..c2544beee
--- /dev/null
+++ b/roms/u-boot/fs/ext4/Makefile
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright 2006
+# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+#
+# (C) Copyright 2003
+# Pavel Bartusek, Sysgo Real-Time Solutions AG, pba@sysgo.de
+#
+
+obj-y := ext4fs.o ext4_common.o dev.o
+obj-$(CONFIG_EXT4_WRITE) += ext4_write.o ext4_journal.o crc16.o
diff --git a/roms/u-boot/fs/ext4/crc16.c b/roms/u-boot/fs/ext4/crc16.c
new file mode 100644
index 000000000..3afb34dae
--- /dev/null
+++ b/roms/u-boot/fs/ext4/crc16.c
@@ -0,0 +1,62 @@
+/*
+ * crc16.c
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#include <common.h>
+#include <asm/byteorder.h>
+#include <linux/stat.h>
+#include "crc16.h"
+
+/** CRC table for the CRC-16. The poly is 0x8005 (x16 + x15 + x2 + 1) */
+static __u16 const crc16_table[256] = {
+ 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
+ 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
+ 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
+ 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
+ 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
+ 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
+ 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
+ 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
+ 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
+ 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
+ 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
+ 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
+ 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
+ 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
+ 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
+ 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
+ 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
+ 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
+ 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
+ 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
+ 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
+ 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
+ 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
+ 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
+ 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
+ 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
+ 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
+ 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
+ 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
+ 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
+ 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
+ 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
+};
+
+/**
+ * Compute the CRC-16 for the data buffer
+*/
+
+unsigned int ext2fs_crc16(unsigned int crc,
+ const void *buffer, unsigned int len)
+{
+ const unsigned char *cp = buffer;
+
+ while (len--)
+ crc = (((crc >> 8) & 0xffU) ^
+ crc16_table[(crc ^ *cp++) & 0xffU]) & 0x0000ffffU;
+ return crc;
+}
diff --git a/roms/u-boot/fs/ext4/crc16.h b/roms/u-boot/fs/ext4/crc16.h
new file mode 100644
index 000000000..5fd113a56
--- /dev/null
+++ b/roms/u-boot/fs/ext4/crc16.h
@@ -0,0 +1,16 @@
+/*
+ * crc16.h - CRC-16 routine
+ * Implements the standard CRC-16:
+ * Width 16
+ * Poly 0x8005 (x16 + x15 + x2 + 1)
+ * Init 0
+ *
+ * Copyright (c) 2005 Ben Gardner <bgardner@wabtec.com>
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+#ifndef __CRC16_H
+#define __CRC16_H
+extern unsigned int ext2fs_crc16(unsigned int crc,
+ const void *buffer, unsigned int len);
+#endif
diff --git a/roms/u-boot/fs/ext4/dev.c b/roms/u-boot/fs/ext4/dev.c
new file mode 100644
index 000000000..168443de1
--- /dev/null
+++ b/roms/u-boot/fs/ext4/dev.c
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2011 - 2012 Samsung Electronics
+ * EXT4 filesystem implementation in Uboot by
+ * Uma Shankar <uma.shankar@samsung.com>
+ * Manjunatha C Achar <a.manjunatha@samsung.com>
+ *
+ * made from existing ext2/dev.c file of Uboot
+ * (C) Copyright 2004
+ * esd gmbh <www.esd-electronics.com>
+ * Reinhard Arlt <reinhard.arlt@esd-electronics.com>
+ *
+ * based on code of fs/reiserfs/dev.c by
+ *
+ * (C) Copyright 2003 - 2004
+ * Sysgo AG, <www.elinos.com>, Pavel Bartusek <pba@sysgo.com>
+ */
+
+/*
+ * Changelog:
+ * 0.1 - Newly created file for ext4fs support. Taken from
+ * fs/ext2/dev.c file in uboot.
+ */
+
+#include <common.h>
+#include <blk.h>
+#include <config.h>
+#include <fs_internal.h>
+#include <ext4fs.h>
+#include <ext_common.h>
+#include "ext4_common.h"
+#include <log.h>
+
+lbaint_t part_offset;
+
+static struct blk_desc *ext4fs_blk_desc;
+static struct disk_partition *part_info;
+
+void ext4fs_set_blk_dev(struct blk_desc *rbdd, struct disk_partition *info)
+{
+ assert(rbdd->blksz == (1 << rbdd->log2blksz));
+ ext4fs_blk_desc = rbdd;
+ get_fs()->dev_desc = rbdd;
+ part_info = info;
+ part_offset = info->start;
+ get_fs()->total_sect = ((uint64_t)info->size * info->blksz) >>
+ get_fs()->dev_desc->log2blksz;
+}
+
+int ext4fs_devread(lbaint_t sector, int byte_offset, int byte_len,
+ char *buffer)
+{
+ return fs_devread(get_fs()->dev_desc, part_info, sector, byte_offset,
+ byte_len, buffer);
+}
+
+int ext4_read_superblock(char *buffer)
+{
+ struct ext_filesystem *fs = get_fs();
+ int sect = SUPERBLOCK_START >> fs->dev_desc->log2blksz;
+ int off = SUPERBLOCK_START % fs->dev_desc->blksz;
+
+ return ext4fs_devread(sect, off, SUPERBLOCK_SIZE,
+ buffer);
+}
diff --git a/roms/u-boot/fs/ext4/ext4_common.c b/roms/u-boot/fs/ext4/ext4_common.c
new file mode 100644
index 000000000..c52cc400e
--- /dev/null
+++ b/roms/u-boot/fs/ext4/ext4_common.c
@@ -0,0 +1,2424 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2011 - 2012 Samsung Electronics
+ * EXT4 filesystem implementation in Uboot by
+ * Uma Shankar <uma.shankar@samsung.com>
+ * Manjunatha C Achar <a.manjunatha@samsung.com>
+ *
+ * ext4ls and ext4load : Based on ext2 ls load support in Uboot.
+ *
+ * (C) Copyright 2004
+ * esd gmbh <www.esd-electronics.com>
+ * Reinhard Arlt <reinhard.arlt@esd-electronics.com>
+ *
+ * based on code from grub2 fs/ext2.c and fs/fshelp.c by
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+ *
+ * ext4write : Based on generic ext4 protocol.
+ */
+
+#include <common.h>
+#include <blk.h>
+#include <ext_common.h>
+#include <ext4fs.h>
+#include <log.h>
+#include <malloc.h>
+#include <memalign.h>
+#include <part.h>
+#include <stddef.h>
+#include <linux/stat.h>
+#include <linux/time.h>
+#include <asm/byteorder.h>
+#include "ext4_common.h"
+
+struct ext2_data *ext4fs_root;
+struct ext2fs_node *ext4fs_file;
+__le32 *ext4fs_indir1_block;
+int ext4fs_indir1_size;
+int ext4fs_indir1_blkno = -1;
+__le32 *ext4fs_indir2_block;
+int ext4fs_indir2_size;
+int ext4fs_indir2_blkno = -1;
+
+__le32 *ext4fs_indir3_block;
+int ext4fs_indir3_size;
+int ext4fs_indir3_blkno = -1;
+struct ext2_inode *g_parent_inode;
+static int symlinknest;
+
+#if defined(CONFIG_EXT4_WRITE)
+struct ext2_block_group *ext4fs_get_group_descriptor
+ (const struct ext_filesystem *fs, uint32_t bg_idx)
+{
+ return (struct ext2_block_group *)(fs->gdtable + (bg_idx * fs->gdsize));
+}
+
+static inline void ext4fs_sb_free_inodes_dec(struct ext2_sblock *sb)
+{
+ sb->free_inodes = cpu_to_le32(le32_to_cpu(sb->free_inodes) - 1);
+}
+
+static inline void ext4fs_sb_free_blocks_dec(struct ext2_sblock *sb)
+{
+ uint64_t free_blocks = le32_to_cpu(sb->free_blocks);
+ free_blocks += (uint64_t)le32_to_cpu(sb->free_blocks_high) << 32;
+ free_blocks--;
+
+ sb->free_blocks = cpu_to_le32(free_blocks & 0xffffffff);
+ sb->free_blocks_high = cpu_to_le16(free_blocks >> 32);
+}
+
+static inline void ext4fs_bg_free_inodes_dec
+ (struct ext2_block_group *bg, const struct ext_filesystem *fs)
+{
+ uint32_t free_inodes = le16_to_cpu(bg->free_inodes);
+ if (fs->gdsize == 64)
+ free_inodes += le16_to_cpu(bg->free_inodes_high) << 16;
+ free_inodes--;
+
+ bg->free_inodes = cpu_to_le16(free_inodes & 0xffff);
+ if (fs->gdsize == 64)
+ bg->free_inodes_high = cpu_to_le16(free_inodes >> 16);
+}
+
+static inline void ext4fs_bg_free_blocks_dec
+ (struct ext2_block_group *bg, const struct ext_filesystem *fs)
+{
+ uint32_t free_blocks = le16_to_cpu(bg->free_blocks);
+ if (fs->gdsize == 64)
+ free_blocks += le16_to_cpu(bg->free_blocks_high) << 16;
+ free_blocks--;
+
+ bg->free_blocks = cpu_to_le16(free_blocks & 0xffff);
+ if (fs->gdsize == 64)
+ bg->free_blocks_high = cpu_to_le16(free_blocks >> 16);
+}
+
+static inline void ext4fs_bg_itable_unused_dec
+ (struct ext2_block_group *bg, const struct ext_filesystem *fs)
+{
+ uint32_t free_inodes = le16_to_cpu(bg->bg_itable_unused);
+ if (fs->gdsize == 64)
+ free_inodes += le16_to_cpu(bg->bg_itable_unused_high) << 16;
+ free_inodes--;
+
+ bg->bg_itable_unused = cpu_to_le16(free_inodes & 0xffff);
+ if (fs->gdsize == 64)
+ bg->bg_itable_unused_high = cpu_to_le16(free_inodes >> 16);
+}
+
+uint64_t ext4fs_sb_get_free_blocks(const struct ext2_sblock *sb)
+{
+ uint64_t free_blocks = le32_to_cpu(sb->free_blocks);
+ free_blocks += (uint64_t)le32_to_cpu(sb->free_blocks_high) << 32;
+ return free_blocks;
+}
+
+void ext4fs_sb_set_free_blocks(struct ext2_sblock *sb, uint64_t free_blocks)
+{
+ sb->free_blocks = cpu_to_le32(free_blocks & 0xffffffff);
+ sb->free_blocks_high = cpu_to_le16(free_blocks >> 32);
+}
+
+uint32_t ext4fs_bg_get_free_blocks(const struct ext2_block_group *bg,
+ const struct ext_filesystem *fs)
+{
+ uint32_t free_blocks = le16_to_cpu(bg->free_blocks);
+ if (fs->gdsize == 64)
+ free_blocks += le16_to_cpu(bg->free_blocks_high) << 16;
+ return free_blocks;
+}
+
+static inline
+uint32_t ext4fs_bg_get_free_inodes(const struct ext2_block_group *bg,
+ const struct ext_filesystem *fs)
+{
+ uint32_t free_inodes = le16_to_cpu(bg->free_inodes);
+ if (fs->gdsize == 64)
+ free_inodes += le16_to_cpu(bg->free_inodes_high) << 16;
+ return free_inodes;
+}
+
+static inline uint16_t ext4fs_bg_get_flags(const struct ext2_block_group *bg)
+{
+ return le16_to_cpu(bg->bg_flags);
+}
+
+static inline void ext4fs_bg_set_flags(struct ext2_block_group *bg,
+ uint16_t flags)
+{
+ bg->bg_flags = cpu_to_le16(flags);
+}
+
+/* Block number of the block bitmap */
+uint64_t ext4fs_bg_get_block_id(const struct ext2_block_group *bg,
+ const struct ext_filesystem *fs)
+{
+ uint64_t block_nr = le32_to_cpu(bg->block_id);
+ if (fs->gdsize == 64)
+ block_nr += (uint64_t)le32_to_cpu(bg->block_id_high) << 32;
+ return block_nr;
+}
+
+/* Block number of the inode bitmap */
+uint64_t ext4fs_bg_get_inode_id(const struct ext2_block_group *bg,
+ const struct ext_filesystem *fs)
+{
+ uint64_t block_nr = le32_to_cpu(bg->inode_id);
+ if (fs->gdsize == 64)
+ block_nr += (uint64_t)le32_to_cpu(bg->inode_id_high) << 32;
+ return block_nr;
+}
+#endif
+
+/* Block number of the inode table */
+uint64_t ext4fs_bg_get_inode_table_id(const struct ext2_block_group *bg,
+ const struct ext_filesystem *fs)
+{
+ uint64_t block_nr = le32_to_cpu(bg->inode_table_id);
+ if (fs->gdsize == 64)
+ block_nr +=
+ (uint64_t)le32_to_cpu(bg->inode_table_id_high) << 32;
+ return block_nr;
+}
+
+#if defined(CONFIG_EXT4_WRITE)
+uint32_t ext4fs_div_roundup(uint32_t size, uint32_t n)
+{
+ uint32_t res = size / n;
+ if (res * n != size)
+ res++;
+
+ return res;
+}
+
+void put_ext4(uint64_t off, const void *buf, uint32_t size)
+{
+ uint64_t startblock;
+ uint64_t remainder;
+ unsigned char *temp_ptr = NULL;
+ struct ext_filesystem *fs = get_fs();
+ int log2blksz = fs->dev_desc->log2blksz;
+ ALLOC_CACHE_ALIGN_BUFFER(unsigned char, sec_buf, fs->dev_desc->blksz);
+
+ startblock = off >> log2blksz;
+ startblock += part_offset;
+ remainder = off & (uint64_t)(fs->dev_desc->blksz - 1);
+
+ if (fs->dev_desc == NULL)
+ return;
+
+ if ((startblock + (size >> log2blksz)) >
+ (part_offset + fs->total_sect)) {
+ printf("part_offset is " LBAFU "\n", part_offset);
+ printf("total_sector is %llu\n", fs->total_sect);
+ printf("error: overflow occurs\n");
+ return;
+ }
+
+ if (remainder) {
+ blk_dread(fs->dev_desc, startblock, 1, sec_buf);
+ temp_ptr = sec_buf;
+ memcpy((temp_ptr + remainder), (unsigned char *)buf, size);
+ blk_dwrite(fs->dev_desc, startblock, 1, sec_buf);
+ } else {
+ if (size >> log2blksz != 0) {
+ blk_dwrite(fs->dev_desc, startblock, size >> log2blksz,
+ (unsigned long *)buf);
+ } else {
+ blk_dread(fs->dev_desc, startblock, 1, sec_buf);
+ temp_ptr = sec_buf;
+ memcpy(temp_ptr, buf, size);
+ blk_dwrite(fs->dev_desc, startblock, 1,
+ (unsigned long *)sec_buf);
+ }
+ }
+}
+
+static int _get_new_inode_no(unsigned char *buffer)
+{
+ struct ext_filesystem *fs = get_fs();
+ unsigned char input;
+ int operand, status;
+ int count = 1;
+ int j = 0;
+
+ /* get the blocksize of the filesystem */
+ unsigned char *ptr = buffer;
+ while (*ptr == 255) {
+ ptr++;
+ count += 8;
+ if (count > le32_to_cpu(ext4fs_root->sblock.inodes_per_group))
+ return -1;
+ }
+
+ for (j = 0; j < fs->blksz; j++) {
+ input = *ptr;
+ int i = 0;
+ while (i <= 7) {
+ operand = 1 << i;
+ status = input & operand;
+ if (status) {
+ i++;
+ count++;
+ } else {
+ *ptr |= operand;
+ return count;
+ }
+ }
+ ptr = ptr + 1;
+ }
+
+ return -1;
+}
+
+static int _get_new_blk_no(unsigned char *buffer)
+{
+ int operand;
+ int count = 0;
+ int i;
+ unsigned char *ptr = buffer;
+ struct ext_filesystem *fs = get_fs();
+
+ while (*ptr == 255) {
+ ptr++;
+ count += 8;
+ if (count == (fs->blksz * 8))
+ return -1;
+ }
+
+ if (fs->blksz == 1024)
+ count += 1;
+
+ for (i = 0; i <= 7; i++) {
+ operand = 1 << i;
+ if (*ptr & operand) {
+ count++;
+ } else {
+ *ptr |= operand;
+ return count;
+ }
+ }
+
+ return -1;
+}
+
+int ext4fs_set_block_bmap(long int blockno, unsigned char *buffer, int index)
+{
+ int i, remainder, status;
+ unsigned char *ptr = buffer;
+ unsigned char operand;
+ i = blockno / 8;
+ remainder = blockno % 8;
+ int blocksize = EXT2_BLOCK_SIZE(ext4fs_root);
+
+ i = i - (index * blocksize);
+ if (blocksize != 1024) {
+ ptr = ptr + i;
+ operand = 1 << remainder;
+ status = *ptr & operand;
+ if (status)
+ return -1;
+
+ *ptr = *ptr | operand;
+ return 0;
+ } else {
+ if (remainder == 0) {
+ ptr = ptr + i - 1;
+ operand = (1 << 7);
+ } else {
+ ptr = ptr + i;
+ operand = (1 << (remainder - 1));
+ }
+ status = *ptr & operand;
+ if (status)
+ return -1;
+
+ *ptr = *ptr | operand;
+ return 0;
+ }
+}
+
+void ext4fs_reset_block_bmap(long int blockno, unsigned char *buffer, int index)
+{
+ int i, remainder, status;
+ unsigned char *ptr = buffer;
+ unsigned char operand;
+ i = blockno / 8;
+ remainder = blockno % 8;
+ int blocksize = EXT2_BLOCK_SIZE(ext4fs_root);
+
+ i = i - (index * blocksize);
+ if (blocksize != 1024) {
+ ptr = ptr + i;
+ operand = (1 << remainder);
+ status = *ptr & operand;
+ if (status)
+ *ptr = *ptr & ~(operand);
+ } else {
+ if (remainder == 0) {
+ ptr = ptr + i - 1;
+ operand = (1 << 7);
+ } else {
+ ptr = ptr + i;
+ operand = (1 << (remainder - 1));
+ }
+ status = *ptr & operand;
+ if (status)
+ *ptr = *ptr & ~(operand);
+ }
+}
+
+int ext4fs_set_inode_bmap(int inode_no, unsigned char *buffer, int index)
+{
+ int i, remainder, status;
+ unsigned char *ptr = buffer;
+ unsigned char operand;
+
+ inode_no -= (index * le32_to_cpu(ext4fs_root->sblock.inodes_per_group));
+ i = inode_no / 8;
+ remainder = inode_no % 8;
+ if (remainder == 0) {
+ ptr = ptr + i - 1;
+ operand = (1 << 7);
+ } else {
+ ptr = ptr + i;
+ operand = (1 << (remainder - 1));
+ }
+ status = *ptr & operand;
+ if (status)
+ return -1;
+
+ *ptr = *ptr | operand;
+
+ return 0;
+}
+
+void ext4fs_reset_inode_bmap(int inode_no, unsigned char *buffer, int index)
+{
+ int i, remainder, status;
+ unsigned char *ptr = buffer;
+ unsigned char operand;
+
+ inode_no -= (index * le32_to_cpu(ext4fs_root->sblock.inodes_per_group));
+ i = inode_no / 8;
+ remainder = inode_no % 8;
+ if (remainder == 0) {
+ ptr = ptr + i - 1;
+ operand = (1 << 7);
+ } else {
+ ptr = ptr + i;
+ operand = (1 << (remainder - 1));
+ }
+ status = *ptr & operand;
+ if (status)
+ *ptr = *ptr & ~(operand);
+}
+
+uint16_t ext4fs_checksum_update(uint32_t i)
+{
+ struct ext2_block_group *desc;
+ struct ext_filesystem *fs = get_fs();
+ uint16_t crc = 0;
+ __le32 le32_i = cpu_to_le32(i);
+
+ desc = ext4fs_get_group_descriptor(fs, i);
+ if (le32_to_cpu(fs->sb->feature_ro_compat) & EXT4_FEATURE_RO_COMPAT_GDT_CSUM) {
+ int offset = offsetof(struct ext2_block_group, bg_checksum);
+
+ crc = ext2fs_crc16(~0, fs->sb->unique_id,
+ sizeof(fs->sb->unique_id));
+ crc = ext2fs_crc16(crc, &le32_i, sizeof(le32_i));
+ crc = ext2fs_crc16(crc, desc, offset);
+ offset += sizeof(desc->bg_checksum); /* skip checksum */
+ assert(offset == sizeof(*desc));
+ if (offset < fs->gdsize) {
+ crc = ext2fs_crc16(crc, (__u8 *)desc + offset,
+ fs->gdsize - offset);
+ }
+ }
+
+ return crc;
+}
+
+static int check_void_in_dentry(struct ext2_dirent *dir, char *filename)
+{
+ int dentry_length;
+ int sizeof_void_space;
+ int new_entry_byte_reqd;
+ short padding_factor = 0;
+
+ if (dir->namelen % 4 != 0)
+ padding_factor = 4 - (dir->namelen % 4);
+
+ dentry_length = sizeof(struct ext2_dirent) +
+ dir->namelen + padding_factor;
+ sizeof_void_space = le16_to_cpu(dir->direntlen) - dentry_length;
+ if (sizeof_void_space == 0)
+ return 0;
+
+ padding_factor = 0;
+ if (strlen(filename) % 4 != 0)
+ padding_factor = 4 - (strlen(filename) % 4);
+
+ new_entry_byte_reqd = strlen(filename) +
+ sizeof(struct ext2_dirent) + padding_factor;
+ if (sizeof_void_space >= new_entry_byte_reqd) {
+ dir->direntlen = cpu_to_le16(dentry_length);
+ return sizeof_void_space;
+ }
+
+ return 0;
+}
+
+int ext4fs_update_parent_dentry(char *filename, int file_type)
+{
+ unsigned int *zero_buffer = NULL;
+ char *root_first_block_buffer = NULL;
+ int blk_idx;
+ long int first_block_no_of_root = 0;
+ int totalbytes = 0;
+ unsigned int new_entry_byte_reqd;
+ int sizeof_void_space = 0;
+ int templength = 0;
+ int inodeno = -1;
+ int status;
+ struct ext_filesystem *fs = get_fs();
+ /* directory entry */
+ struct ext2_dirent *dir;
+ char *temp_dir = NULL;
+ uint32_t new_blk_no;
+ uint32_t new_size;
+ uint32_t new_blockcnt;
+ uint32_t directory_blocks;
+
+ zero_buffer = zalloc(fs->blksz);
+ if (!zero_buffer) {
+ printf("No Memory\n");
+ return -1;
+ }
+ root_first_block_buffer = zalloc(fs->blksz);
+ if (!root_first_block_buffer) {
+ free(zero_buffer);
+ printf("No Memory\n");
+ return -1;
+ }
+ new_entry_byte_reqd = ROUND(strlen(filename) +
+ sizeof(struct ext2_dirent), 4);
+restart:
+ directory_blocks = le32_to_cpu(g_parent_inode->size) >>
+ LOG2_BLOCK_SIZE(ext4fs_root);
+ blk_idx = directory_blocks - 1;
+
+restart_read:
+ /* read the block no allocated to a file */
+ first_block_no_of_root = read_allocated_block(g_parent_inode, blk_idx,
+ NULL);
+ if (first_block_no_of_root <= 0)
+ goto fail;
+
+ status = ext4fs_devread((lbaint_t)first_block_no_of_root
+ * fs->sect_perblk,
+ 0, fs->blksz, root_first_block_buffer);
+ if (status == 0)
+ goto fail;
+
+ if (ext4fs_log_journal(root_first_block_buffer, first_block_no_of_root))
+ goto fail;
+ dir = (struct ext2_dirent *)root_first_block_buffer;
+ totalbytes = 0;
+
+ while (le16_to_cpu(dir->direntlen) > 0) {
+ unsigned short used_len = ROUND(dir->namelen +
+ sizeof(struct ext2_dirent), 4);
+
+ /* last entry of block */
+ if (fs->blksz - totalbytes == le16_to_cpu(dir->direntlen)) {
+
+ /* check if new entry fits */
+ if ((used_len + new_entry_byte_reqd) <=
+ le16_to_cpu(dir->direntlen)) {
+ dir->direntlen = cpu_to_le16(used_len);
+ break;
+ } else {
+ if (blk_idx > 0) {
+ printf("Block full, trying previous\n");
+ blk_idx--;
+ goto restart_read;
+ }
+ printf("All blocks full: Allocate new\n");
+
+ if (le32_to_cpu(g_parent_inode->flags) &
+ EXT4_EXTENTS_FL) {
+ printf("Directory uses extents\n");
+ goto fail;
+ }
+ if (directory_blocks >= INDIRECT_BLOCKS) {
+ printf("Directory exceeds limit\n");
+ goto fail;
+ }
+ new_blk_no = ext4fs_get_new_blk_no();
+ if (new_blk_no == -1) {
+ printf("no block left to assign\n");
+ goto fail;
+ }
+ put_ext4((uint64_t)new_blk_no * fs->blksz, zero_buffer, fs->blksz);
+ g_parent_inode->b.blocks.
+ dir_blocks[directory_blocks] =
+ cpu_to_le32(new_blk_no);
+
+ new_size = le32_to_cpu(g_parent_inode->size);
+ new_size += fs->blksz;
+ g_parent_inode->size = cpu_to_le32(new_size);
+
+ new_blockcnt = le32_to_cpu(g_parent_inode->blockcnt);
+ new_blockcnt += fs->blksz >> LOG2_SECTOR_SIZE;
+ g_parent_inode->blockcnt = cpu_to_le32(new_blockcnt);
+
+ if (ext4fs_put_metadata
+ (root_first_block_buffer,
+ first_block_no_of_root))
+ goto fail;
+ goto restart;
+ }
+ }
+
+ templength = le16_to_cpu(dir->direntlen);
+ totalbytes = totalbytes + templength;
+ sizeof_void_space = check_void_in_dentry(dir, filename);
+ if (sizeof_void_space)
+ break;
+
+ dir = (struct ext2_dirent *)((char *)dir + templength);
+ }
+
+ /* make a pointer ready for creating next directory entry */
+ templength = le16_to_cpu(dir->direntlen);
+ totalbytes = totalbytes + templength;
+ dir = (struct ext2_dirent *)((char *)dir + templength);
+
+ /* get the next available inode number */
+ inodeno = ext4fs_get_new_inode_no();
+ if (inodeno == -1) {
+ printf("no inode left to assign\n");
+ goto fail;
+ }
+ dir->inode = cpu_to_le32(inodeno);
+ if (sizeof_void_space)
+ dir->direntlen = cpu_to_le16(sizeof_void_space);
+ else
+ dir->direntlen = cpu_to_le16(fs->blksz - totalbytes);
+
+ dir->namelen = strlen(filename);
+ dir->filetype = file_type;
+ temp_dir = (char *)dir;
+ temp_dir = temp_dir + sizeof(struct ext2_dirent);
+ memcpy(temp_dir, filename, strlen(filename));
+
+ /* update or write the 1st block of root inode */
+ if (ext4fs_put_metadata(root_first_block_buffer,
+ first_block_no_of_root))
+ goto fail;
+
+fail:
+ free(zero_buffer);
+ free(root_first_block_buffer);
+
+ return inodeno;
+}
+
+static int search_dir(struct ext2_inode *parent_inode, char *dirname)
+{
+ int status;
+ int inodeno = 0;
+ int offset;
+ int blk_idx;
+ long int blknr;
+ char *block_buffer = NULL;
+ struct ext2_dirent *dir = NULL;
+ struct ext_filesystem *fs = get_fs();
+ uint32_t directory_blocks;
+ char *direntname;
+
+ directory_blocks = le32_to_cpu(parent_inode->size) >>
+ LOG2_BLOCK_SIZE(ext4fs_root);
+
+ block_buffer = zalloc(fs->blksz);
+ if (!block_buffer)
+ goto fail;
+
+ /* get the block no allocated to a file */
+ for (blk_idx = 0; blk_idx < directory_blocks; blk_idx++) {
+ blknr = read_allocated_block(parent_inode, blk_idx, NULL);
+ if (blknr <= 0)
+ goto fail;
+
+ /* read the directory block */
+ status = ext4fs_devread((lbaint_t)blknr * fs->sect_perblk,
+ 0, fs->blksz, (char *)block_buffer);
+ if (status == 0)
+ goto fail;
+
+ offset = 0;
+ do {
+ if (offset & 3) {
+ printf("Badly aligned ext2_dirent\n");
+ break;
+ }
+
+ dir = (struct ext2_dirent *)(block_buffer + offset);
+ direntname = (char*)(dir) + sizeof(struct ext2_dirent);
+
+ int direntlen = le16_to_cpu(dir->direntlen);
+ if (direntlen < sizeof(struct ext2_dirent))
+ break;
+
+ if (dir->inode && (strlen(dirname) == dir->namelen) &&
+ (strncmp(dirname, direntname, dir->namelen) == 0)) {
+ inodeno = le32_to_cpu(dir->inode);
+ break;
+ }
+
+ offset += direntlen;
+
+ } while (offset < fs->blksz);
+
+ if (inodeno > 0) {
+ free(block_buffer);
+ return inodeno;
+ }
+ }
+
+fail:
+ free(block_buffer);
+
+ return -1;
+}
+
+static int find_dir_depth(char *dirname)
+{
+ char *token = strtok(dirname, "/");
+ int count = 0;
+ while (token != NULL) {
+ token = strtok(NULL, "/");
+ count++;
+ }
+ return count + 1 + 1;
+ /*
+ * for example for string /home/temp
+ * depth=home(1)+temp(1)+1 extra for NULL;
+ * so count is 4;
+ */
+}
+
+static int parse_path(char **arr, char *dirname)
+{
+ char *token = strtok(dirname, "/");
+ int i = 0;
+
+ /* add root */
+ arr[i] = zalloc(strlen("/") + 1);
+ if (!arr[i])
+ return -ENOMEM;
+ memcpy(arr[i++], "/", strlen("/"));
+
+ /* add each path entry after root */
+ while (token != NULL) {
+ arr[i] = zalloc(strlen(token) + 1);
+ if (!arr[i])
+ return -ENOMEM;
+ memcpy(arr[i++], token, strlen(token));
+ token = strtok(NULL, "/");
+ }
+ arr[i] = NULL;
+
+ return 0;
+}
+
+int ext4fs_iget(int inode_no, struct ext2_inode *inode)
+{
+ if (ext4fs_read_inode(ext4fs_root, inode_no, inode) == 0)
+ return -1;
+
+ return 0;
+}
+
+/*
+ * Function: ext4fs_get_parent_inode_num
+ * Return Value: inode Number of the parent directory of file/Directory to be
+ * created
+ * dirname : Input parmater, input path name of the file/directory to be created
+ * dname : Output parameter, to be filled with the name of the directory
+ * extracted from dirname
+ */
+int ext4fs_get_parent_inode_num(const char *dirname, char *dname, int flags)
+{
+ int i;
+ int depth = 0;
+ int matched_inode_no;
+ int result_inode_no = -1;
+ char **ptr = NULL;
+ char *depth_dirname = NULL;
+ char *parse_dirname = NULL;
+ struct ext2_inode *parent_inode = NULL;
+ struct ext2_inode *first_inode = NULL;
+ struct ext2_inode temp_inode;
+
+ if (*dirname != '/') {
+ printf("Please supply Absolute path\n");
+ return -1;
+ }
+
+ /* TODO: input validation make equivalent to linux */
+ depth_dirname = zalloc(strlen(dirname) + 1);
+ if (!depth_dirname)
+ return -ENOMEM;
+
+ memcpy(depth_dirname, dirname, strlen(dirname));
+ depth = find_dir_depth(depth_dirname);
+ parse_dirname = zalloc(strlen(dirname) + 1);
+ if (!parse_dirname)
+ goto fail;
+ memcpy(parse_dirname, dirname, strlen(dirname));
+
+ /* allocate memory for each directory level */
+ ptr = zalloc((depth) * sizeof(char *));
+ if (!ptr)
+ goto fail;
+ if (parse_path(ptr, parse_dirname))
+ goto fail;
+ parent_inode = zalloc(sizeof(struct ext2_inode));
+ if (!parent_inode)
+ goto fail;
+ first_inode = zalloc(sizeof(struct ext2_inode));
+ if (!first_inode)
+ goto fail;
+ memcpy(parent_inode, ext4fs_root->inode, sizeof(struct ext2_inode));
+ memcpy(first_inode, parent_inode, sizeof(struct ext2_inode));
+ if (flags & F_FILE)
+ result_inode_no = EXT2_ROOT_INO;
+ for (i = 1; i < depth; i++) {
+ matched_inode_no = search_dir(parent_inode, ptr[i]);
+ if (matched_inode_no == -1) {
+ if (ptr[i + 1] == NULL && i == 1) {
+ result_inode_no = EXT2_ROOT_INO;
+ goto end;
+ } else {
+ if (ptr[i + 1] == NULL)
+ break;
+ printf("Invalid path\n");
+ result_inode_no = -1;
+ goto fail;
+ }
+ } else {
+ if (ptr[i + 1] != NULL) {
+ memset(parent_inode, '\0',
+ sizeof(struct ext2_inode));
+ if (ext4fs_iget(matched_inode_no,
+ parent_inode)) {
+ result_inode_no = -1;
+ goto fail;
+ }
+ result_inode_no = matched_inode_no;
+ } else {
+ break;
+ }
+ }
+ }
+
+end:
+ if (i == 1)
+ matched_inode_no = search_dir(first_inode, ptr[i]);
+ else
+ matched_inode_no = search_dir(parent_inode, ptr[i]);
+
+ if (matched_inode_no != -1) {
+ ext4fs_iget(matched_inode_no, &temp_inode);
+ if (le16_to_cpu(temp_inode.mode) & S_IFDIR) {
+ printf("It is a Directory\n");
+ result_inode_no = -1;
+ goto fail;
+ }
+ }
+
+ if (strlen(ptr[i]) > 256) {
+ result_inode_no = -1;
+ goto fail;
+ }
+ memcpy(dname, ptr[i], strlen(ptr[i]));
+
+fail:
+ free(depth_dirname);
+ free(parse_dirname);
+ for (i = 0; i < depth; i++) {
+ if (!ptr[i])
+ break;
+ free(ptr[i]);
+ }
+ free(ptr);
+ free(parent_inode);
+ free(first_inode);
+
+ return result_inode_no;
+}
+
+static int unlink_filename(char *filename, unsigned int blknr)
+{
+ int status;
+ int inodeno = 0;
+ int offset;
+ char *block_buffer = NULL;
+ struct ext2_dirent *dir = NULL;
+ struct ext2_dirent *previous_dir;
+ struct ext_filesystem *fs = get_fs();
+ int ret = -1;
+ char *direntname;
+
+ block_buffer = zalloc(fs->blksz);
+ if (!block_buffer)
+ return -ENOMEM;
+
+ /* read the directory block */
+ status = ext4fs_devread((lbaint_t)blknr * fs->sect_perblk, 0,
+ fs->blksz, block_buffer);
+ if (status == 0)
+ goto fail;
+
+ offset = 0;
+ do {
+ if (offset & 3) {
+ printf("Badly aligned ext2_dirent\n");
+ break;
+ }
+
+ previous_dir = dir;
+ dir = (struct ext2_dirent *)(block_buffer + offset);
+ direntname = (char *)(dir) + sizeof(struct ext2_dirent);
+
+ int direntlen = le16_to_cpu(dir->direntlen);
+ if (direntlen < sizeof(struct ext2_dirent))
+ break;
+
+ if (dir->inode && (strlen(filename) == dir->namelen) &&
+ (strncmp(direntname, filename, dir->namelen) == 0)) {
+ inodeno = le32_to_cpu(dir->inode);
+ break;
+ }
+
+ offset += direntlen;
+
+ } while (offset < fs->blksz);
+
+ if (inodeno > 0) {
+ printf("file found, deleting\n");
+ if (ext4fs_log_journal(block_buffer, blknr))
+ goto fail;
+
+ if (previous_dir) {
+ /* merge dir entry with predecessor */
+ uint16_t new_len;
+ new_len = le16_to_cpu(previous_dir->direntlen);
+ new_len += le16_to_cpu(dir->direntlen);
+ previous_dir->direntlen = cpu_to_le16(new_len);
+ } else {
+ /* invalidate dir entry */
+ dir->inode = 0;
+ }
+ if (ext4fs_put_metadata(block_buffer, blknr))
+ goto fail;
+ ret = inodeno;
+ }
+fail:
+ free(block_buffer);
+
+ return ret;
+}
+
+int ext4fs_filename_unlink(char *filename)
+{
+ int blk_idx;
+ long int blknr = -1;
+ int inodeno = -1;
+ uint32_t directory_blocks;
+
+ directory_blocks = le32_to_cpu(g_parent_inode->size) >>
+ LOG2_BLOCK_SIZE(ext4fs_root);
+
+ /* read the block no allocated to a file */
+ for (blk_idx = 0; blk_idx < directory_blocks; blk_idx++) {
+ blknr = read_allocated_block(g_parent_inode, blk_idx, NULL);
+ if (blknr <= 0)
+ break;
+ inodeno = unlink_filename(filename, blknr);
+ if (inodeno != -1)
+ return inodeno;
+ }
+
+ return -1;
+}
+
+uint32_t ext4fs_get_new_blk_no(void)
+{
+ short i;
+ short status;
+ int remainder;
+ unsigned int bg_idx;
+ static int prev_bg_bitmap_index = -1;
+ unsigned int blk_per_grp = le32_to_cpu(ext4fs_root->sblock.blocks_per_group);
+ struct ext_filesystem *fs = get_fs();
+ char *journal_buffer = zalloc(fs->blksz);
+ char *zero_buffer = zalloc(fs->blksz);
+ if (!journal_buffer || !zero_buffer)
+ goto fail;
+
+ if (fs->first_pass_bbmap == 0) {
+ for (i = 0; i < fs->no_blkgrp; i++) {
+ struct ext2_block_group *bgd = NULL;
+ bgd = ext4fs_get_group_descriptor(fs, i);
+ if (ext4fs_bg_get_free_blocks(bgd, fs)) {
+ uint16_t bg_flags = ext4fs_bg_get_flags(bgd);
+ uint64_t b_bitmap_blk =
+ ext4fs_bg_get_block_id(bgd, fs);
+ if (bg_flags & EXT4_BG_BLOCK_UNINIT) {
+ memcpy(fs->blk_bmaps[i], zero_buffer,
+ fs->blksz);
+ put_ext4(b_bitmap_blk * fs->blksz,
+ fs->blk_bmaps[i], fs->blksz);
+ bg_flags &= ~EXT4_BG_BLOCK_UNINIT;
+ ext4fs_bg_set_flags(bgd, bg_flags);
+ }
+ fs->curr_blkno =
+ _get_new_blk_no(fs->blk_bmaps[i]);
+ if (fs->curr_blkno == -1)
+ /* block bitmap is completely filled */
+ continue;
+ fs->curr_blkno = fs->curr_blkno +
+ (i * fs->blksz * 8);
+ fs->first_pass_bbmap++;
+ ext4fs_bg_free_blocks_dec(bgd, fs);
+ ext4fs_sb_free_blocks_dec(fs->sb);
+ status = ext4fs_devread(b_bitmap_blk *
+ fs->sect_perblk,
+ 0, fs->blksz,
+ journal_buffer);
+ if (status == 0)
+ goto fail;
+ if (ext4fs_log_journal(journal_buffer,
+ b_bitmap_blk))
+ goto fail;
+ goto success;
+ } else {
+ debug("no space left on block group %d\n", i);
+ }
+ }
+
+ goto fail;
+ } else {
+ fs->curr_blkno++;
+restart:
+ /* get the blockbitmap index respective to blockno */
+ bg_idx = fs->curr_blkno / blk_per_grp;
+ if (fs->blksz == 1024) {
+ remainder = fs->curr_blkno % blk_per_grp;
+ if (!remainder)
+ bg_idx--;
+ }
+
+ /*
+ * To skip completely filled block group bitmaps
+ * Optimize the block allocation
+ */
+ if (bg_idx >= fs->no_blkgrp)
+ goto fail;
+
+ struct ext2_block_group *bgd = NULL;
+ bgd = ext4fs_get_group_descriptor(fs, bg_idx);
+ if (ext4fs_bg_get_free_blocks(bgd, fs) == 0) {
+ debug("block group %u is full. Skipping\n", bg_idx);
+ fs->curr_blkno = (bg_idx + 1) * blk_per_grp;
+ if (fs->blksz == 1024)
+ fs->curr_blkno += 1;
+ goto restart;
+ }
+
+ uint16_t bg_flags = ext4fs_bg_get_flags(bgd);
+ uint64_t b_bitmap_blk = ext4fs_bg_get_block_id(bgd, fs);
+ if (bg_flags & EXT4_BG_BLOCK_UNINIT) {
+ memcpy(fs->blk_bmaps[bg_idx], zero_buffer, fs->blksz);
+ put_ext4(b_bitmap_blk * fs->blksz,
+ zero_buffer, fs->blksz);
+ bg_flags &= ~EXT4_BG_BLOCK_UNINIT;
+ ext4fs_bg_set_flags(bgd, bg_flags);
+ }
+
+ if (ext4fs_set_block_bmap(fs->curr_blkno, fs->blk_bmaps[bg_idx],
+ bg_idx) != 0) {
+ debug("going for restart for the block no %ld %u\n",
+ fs->curr_blkno, bg_idx);
+ fs->curr_blkno++;
+ goto restart;
+ }
+
+ /* journal backup */
+ if (prev_bg_bitmap_index != bg_idx) {
+ status = ext4fs_devread(b_bitmap_blk * fs->sect_perblk,
+ 0, fs->blksz, journal_buffer);
+ if (status == 0)
+ goto fail;
+ if (ext4fs_log_journal(journal_buffer, b_bitmap_blk))
+ goto fail;
+
+ prev_bg_bitmap_index = bg_idx;
+ }
+ ext4fs_bg_free_blocks_dec(bgd, fs);
+ ext4fs_sb_free_blocks_dec(fs->sb);
+ goto success;
+ }
+success:
+ free(journal_buffer);
+ free(zero_buffer);
+
+ return fs->curr_blkno;
+fail:
+ free(journal_buffer);
+ free(zero_buffer);
+
+ return -1;
+}
+
+int ext4fs_get_new_inode_no(void)
+{
+ short i;
+ short status;
+ unsigned int ibmap_idx;
+ static int prev_inode_bitmap_index = -1;
+ unsigned int inodes_per_grp = le32_to_cpu(ext4fs_root->sblock.inodes_per_group);
+ struct ext_filesystem *fs = get_fs();
+ char *journal_buffer = zalloc(fs->blksz);
+ char *zero_buffer = zalloc(fs->blksz);
+ if (!journal_buffer || !zero_buffer)
+ goto fail;
+ int has_gdt_chksum = le32_to_cpu(fs->sb->feature_ro_compat) &
+ EXT4_FEATURE_RO_COMPAT_GDT_CSUM ? 1 : 0;
+
+ if (fs->first_pass_ibmap == 0) {
+ for (i = 0; i < fs->no_blkgrp; i++) {
+ uint32_t free_inodes;
+ struct ext2_block_group *bgd = NULL;
+ bgd = ext4fs_get_group_descriptor(fs, i);
+ free_inodes = ext4fs_bg_get_free_inodes(bgd, fs);
+ if (free_inodes) {
+ uint16_t bg_flags = ext4fs_bg_get_flags(bgd);
+ uint64_t i_bitmap_blk =
+ ext4fs_bg_get_inode_id(bgd, fs);
+ if (has_gdt_chksum)
+ bgd->bg_itable_unused = free_inodes;
+ if (bg_flags & EXT4_BG_INODE_UNINIT) {
+ put_ext4(i_bitmap_blk * fs->blksz,
+ zero_buffer, fs->blksz);
+ bg_flags &= ~EXT4_BG_INODE_UNINIT;
+ ext4fs_bg_set_flags(bgd, bg_flags);
+ memcpy(fs->inode_bmaps[i],
+ zero_buffer, fs->blksz);
+ }
+ fs->curr_inode_no =
+ _get_new_inode_no(fs->inode_bmaps[i]);
+ if (fs->curr_inode_no == -1)
+ /* inode bitmap is completely filled */
+ continue;
+ fs->curr_inode_no = fs->curr_inode_no +
+ (i * inodes_per_grp);
+ fs->first_pass_ibmap++;
+ ext4fs_bg_free_inodes_dec(bgd, fs);
+ if (has_gdt_chksum)
+ ext4fs_bg_itable_unused_dec(bgd, fs);
+ ext4fs_sb_free_inodes_dec(fs->sb);
+ status = ext4fs_devread(i_bitmap_blk *
+ fs->sect_perblk,
+ 0, fs->blksz,
+ journal_buffer);
+ if (status == 0)
+ goto fail;
+ if (ext4fs_log_journal(journal_buffer,
+ i_bitmap_blk))
+ goto fail;
+ goto success;
+ } else
+ debug("no inode left on block group %d\n", i);
+ }
+ goto fail;
+ } else {
+restart:
+ fs->curr_inode_no++;
+ /* get the blockbitmap index respective to blockno */
+ ibmap_idx = fs->curr_inode_no / inodes_per_grp;
+ struct ext2_block_group *bgd =
+ ext4fs_get_group_descriptor(fs, ibmap_idx);
+ uint16_t bg_flags = ext4fs_bg_get_flags(bgd);
+ uint64_t i_bitmap_blk = ext4fs_bg_get_inode_id(bgd, fs);
+
+ if (bg_flags & EXT4_BG_INODE_UNINIT) {
+ put_ext4(i_bitmap_blk * fs->blksz,
+ zero_buffer, fs->blksz);
+ bg_flags &= ~EXT4_BG_INODE_UNINIT;
+ ext4fs_bg_set_flags(bgd, bg_flags);
+ memcpy(fs->inode_bmaps[ibmap_idx], zero_buffer,
+ fs->blksz);
+ }
+
+ if (ext4fs_set_inode_bmap(fs->curr_inode_no,
+ fs->inode_bmaps[ibmap_idx],
+ ibmap_idx) != 0) {
+ debug("going for restart for the block no %d %u\n",
+ fs->curr_inode_no, ibmap_idx);
+ goto restart;
+ }
+
+ /* journal backup */
+ if (prev_inode_bitmap_index != ibmap_idx) {
+ status = ext4fs_devread(i_bitmap_blk * fs->sect_perblk,
+ 0, fs->blksz, journal_buffer);
+ if (status == 0)
+ goto fail;
+ if (ext4fs_log_journal(journal_buffer,
+ le32_to_cpu(bgd->inode_id)))
+ goto fail;
+ prev_inode_bitmap_index = ibmap_idx;
+ }
+ ext4fs_bg_free_inodes_dec(bgd, fs);
+ if (has_gdt_chksum)
+ bgd->bg_itable_unused = bgd->free_inodes;
+ ext4fs_sb_free_inodes_dec(fs->sb);
+ goto success;
+ }
+
+success:
+ free(journal_buffer);
+ free(zero_buffer);
+
+ return fs->curr_inode_no;
+fail:
+ free(journal_buffer);
+ free(zero_buffer);
+
+ return -1;
+
+}
+
+
+static void alloc_single_indirect_block(struct ext2_inode *file_inode,
+ unsigned int *total_remaining_blocks,
+ unsigned int *no_blks_reqd)
+{
+ short i;
+ short status;
+ long int actual_block_no;
+ long int si_blockno;
+ /* si :single indirect */
+ __le32 *si_buffer = NULL;
+ __le32 *si_start_addr = NULL;
+ struct ext_filesystem *fs = get_fs();
+
+ if (*total_remaining_blocks != 0) {
+ si_buffer = zalloc(fs->blksz);
+ if (!si_buffer) {
+ printf("No Memory\n");
+ return;
+ }
+ si_start_addr = si_buffer;
+ si_blockno = ext4fs_get_new_blk_no();
+ if (si_blockno == -1) {
+ printf("no block left to assign\n");
+ goto fail;
+ }
+ (*no_blks_reqd)++;
+ debug("SIPB %ld: %u\n", si_blockno, *total_remaining_blocks);
+
+ status = ext4fs_devread((lbaint_t)si_blockno * fs->sect_perblk,
+ 0, fs->blksz, (char *)si_buffer);
+ memset(si_buffer, '\0', fs->blksz);
+ if (status == 0)
+ goto fail;
+
+ for (i = 0; i < (fs->blksz / sizeof(int)); i++) {
+ actual_block_no = ext4fs_get_new_blk_no();
+ if (actual_block_no == -1) {
+ printf("no block left to assign\n");
+ goto fail;
+ }
+ *si_buffer = cpu_to_le32(actual_block_no);
+ debug("SIAB %u: %u\n", *si_buffer,
+ *total_remaining_blocks);
+
+ si_buffer++;
+ (*total_remaining_blocks)--;
+ if (*total_remaining_blocks == 0)
+ break;
+ }
+
+ /* write the block to disk */
+ put_ext4(((uint64_t) ((uint64_t)si_blockno * (uint64_t)fs->blksz)),
+ si_start_addr, fs->blksz);
+ file_inode->b.blocks.indir_block = cpu_to_le32(si_blockno);
+ }
+fail:
+ free(si_start_addr);
+}
+
+static void alloc_double_indirect_block(struct ext2_inode *file_inode,
+ unsigned int *total_remaining_blocks,
+ unsigned int *no_blks_reqd)
+{
+ short i;
+ short j;
+ short status;
+ long int actual_block_no;
+ /* di:double indirect */
+ long int di_blockno_parent;
+ long int di_blockno_child;
+ __le32 *di_parent_buffer = NULL;
+ __le32 *di_child_buff = NULL;
+ __le32 *di_block_start_addr = NULL;
+ __le32 *di_child_buff_start = NULL;
+ struct ext_filesystem *fs = get_fs();
+
+ if (*total_remaining_blocks != 0) {
+ /* double indirect parent block connecting to inode */
+ di_blockno_parent = ext4fs_get_new_blk_no();
+ if (di_blockno_parent == -1) {
+ printf("no block left to assign\n");
+ goto fail;
+ }
+ di_parent_buffer = zalloc(fs->blksz);
+ if (!di_parent_buffer)
+ goto fail;
+
+ di_block_start_addr = di_parent_buffer;
+ (*no_blks_reqd)++;
+ debug("DIPB %ld: %u\n", di_blockno_parent,
+ *total_remaining_blocks);
+
+ status = ext4fs_devread((lbaint_t)di_blockno_parent *
+ fs->sect_perblk, 0,
+ fs->blksz, (char *)di_parent_buffer);
+
+ if (!status) {
+ printf("%s: Device read error!\n", __func__);
+ goto fail;
+ }
+ memset(di_parent_buffer, '\0', fs->blksz);
+
+ /*
+ * start:for each double indirect parent
+ * block create one more block
+ */
+ for (i = 0; i < (fs->blksz / sizeof(int)); i++) {
+ di_blockno_child = ext4fs_get_new_blk_no();
+ if (di_blockno_child == -1) {
+ printf("no block left to assign\n");
+ goto fail;
+ }
+ di_child_buff = zalloc(fs->blksz);
+ if (!di_child_buff)
+ goto fail;
+
+ di_child_buff_start = di_child_buff;
+ *di_parent_buffer = cpu_to_le32(di_blockno_child);
+ di_parent_buffer++;
+ (*no_blks_reqd)++;
+ debug("DICB %ld: %u\n", di_blockno_child,
+ *total_remaining_blocks);
+
+ status = ext4fs_devread((lbaint_t)di_blockno_child *
+ fs->sect_perblk, 0,
+ fs->blksz,
+ (char *)di_child_buff);
+
+ if (!status) {
+ printf("%s: Device read error!\n", __func__);
+ goto fail;
+ }
+ memset(di_child_buff, '\0', fs->blksz);
+ /* filling of actual datablocks for each child */
+ for (j = 0; j < (fs->blksz / sizeof(int)); j++) {
+ actual_block_no = ext4fs_get_new_blk_no();
+ if (actual_block_no == -1) {
+ printf("no block left to assign\n");
+ goto fail;
+ }
+ *di_child_buff = cpu_to_le32(actual_block_no);
+ debug("DIAB %ld: %u\n", actual_block_no,
+ *total_remaining_blocks);
+
+ di_child_buff++;
+ (*total_remaining_blocks)--;
+ if (*total_remaining_blocks == 0)
+ break;
+ }
+ /* write the block table */
+ put_ext4(((uint64_t) ((uint64_t)di_blockno_child * (uint64_t)fs->blksz)),
+ di_child_buff_start, fs->blksz);
+ free(di_child_buff_start);
+ di_child_buff_start = NULL;
+
+ if (*total_remaining_blocks == 0)
+ break;
+ }
+ put_ext4(((uint64_t) ((uint64_t)di_blockno_parent * (uint64_t)fs->blksz)),
+ di_block_start_addr, fs->blksz);
+ file_inode->b.blocks.double_indir_block = cpu_to_le32(di_blockno_parent);
+ }
+fail:
+ free(di_block_start_addr);
+}
+
+static void alloc_triple_indirect_block(struct ext2_inode *file_inode,
+ unsigned int *total_remaining_blocks,
+ unsigned int *no_blks_reqd)
+{
+ short i;
+ short j;
+ short k;
+ long int actual_block_no;
+ /* ti: Triple Indirect */
+ long int ti_gp_blockno;
+ long int ti_parent_blockno;
+ long int ti_child_blockno;
+ __le32 *ti_gp_buff = NULL;
+ __le32 *ti_parent_buff = NULL;
+ __le32 *ti_child_buff = NULL;
+ __le32 *ti_gp_buff_start_addr = NULL;
+ __le32 *ti_pbuff_start_addr = NULL;
+ __le32 *ti_cbuff_start_addr = NULL;
+ struct ext_filesystem *fs = get_fs();
+ if (*total_remaining_blocks != 0) {
+ /* triple indirect grand parent block connecting to inode */
+ ti_gp_blockno = ext4fs_get_new_blk_no();
+ if (ti_gp_blockno == -1) {
+ printf("no block left to assign\n");
+ return;
+ }
+ ti_gp_buff = zalloc(fs->blksz);
+ if (!ti_gp_buff)
+ return;
+
+ ti_gp_buff_start_addr = ti_gp_buff;
+ (*no_blks_reqd)++;
+ debug("TIGPB %ld: %u\n", ti_gp_blockno,
+ *total_remaining_blocks);
+
+ /* for each 4 byte grand parent entry create one more block */
+ for (i = 0; i < (fs->blksz / sizeof(int)); i++) {
+ ti_parent_blockno = ext4fs_get_new_blk_no();
+ if (ti_parent_blockno == -1) {
+ printf("no block left to assign\n");
+ goto fail;
+ }
+ ti_parent_buff = zalloc(fs->blksz);
+ if (!ti_parent_buff)
+ goto fail;
+
+ ti_pbuff_start_addr = ti_parent_buff;
+ *ti_gp_buff = cpu_to_le32(ti_parent_blockno);
+ ti_gp_buff++;
+ (*no_blks_reqd)++;
+ debug("TIPB %ld: %u\n", ti_parent_blockno,
+ *total_remaining_blocks);
+
+ /* for each 4 byte entry parent create one more block */
+ for (j = 0; j < (fs->blksz / sizeof(int)); j++) {
+ ti_child_blockno = ext4fs_get_new_blk_no();
+ if (ti_child_blockno == -1) {
+ printf("no block left assign\n");
+ goto fail1;
+ }
+ ti_child_buff = zalloc(fs->blksz);
+ if (!ti_child_buff)
+ goto fail1;
+
+ ti_cbuff_start_addr = ti_child_buff;
+ *ti_parent_buff = cpu_to_le32(ti_child_blockno);
+ ti_parent_buff++;
+ (*no_blks_reqd)++;
+ debug("TICB %ld: %u\n", ti_parent_blockno,
+ *total_remaining_blocks);
+
+ /* fill actual datablocks for each child */
+ for (k = 0; k < (fs->blksz / sizeof(int));
+ k++) {
+ actual_block_no =
+ ext4fs_get_new_blk_no();
+ if (actual_block_no == -1) {
+ printf("no block left\n");
+ free(ti_cbuff_start_addr);
+ goto fail1;
+ }
+ *ti_child_buff = cpu_to_le32(actual_block_no);
+ debug("TIAB %ld: %u\n", actual_block_no,
+ *total_remaining_blocks);
+
+ ti_child_buff++;
+ (*total_remaining_blocks)--;
+ if (*total_remaining_blocks == 0)
+ break;
+ }
+ /* write the child block */
+ put_ext4(((uint64_t) ((uint64_t)ti_child_blockno *
+ (uint64_t)fs->blksz)),
+ ti_cbuff_start_addr, fs->blksz);
+ free(ti_cbuff_start_addr);
+
+ if (*total_remaining_blocks == 0)
+ break;
+ }
+ /* write the parent block */
+ put_ext4(((uint64_t) ((uint64_t)ti_parent_blockno * (uint64_t)fs->blksz)),
+ ti_pbuff_start_addr, fs->blksz);
+ free(ti_pbuff_start_addr);
+
+ if (*total_remaining_blocks == 0)
+ break;
+ }
+ /* write the grand parent block */
+ put_ext4(((uint64_t) ((uint64_t)ti_gp_blockno * (uint64_t)fs->blksz)),
+ ti_gp_buff_start_addr, fs->blksz);
+ file_inode->b.blocks.triple_indir_block = cpu_to_le32(ti_gp_blockno);
+ free(ti_gp_buff_start_addr);
+ return;
+ }
+fail1:
+ free(ti_pbuff_start_addr);
+fail:
+ free(ti_gp_buff_start_addr);
+}
+
+void ext4fs_allocate_blocks(struct ext2_inode *file_inode,
+ unsigned int total_remaining_blocks,
+ unsigned int *total_no_of_block)
+{
+ short i;
+ long int direct_blockno;
+ unsigned int no_blks_reqd = 0;
+
+ /* allocation of direct blocks */
+ for (i = 0; total_remaining_blocks && i < INDIRECT_BLOCKS; i++) {
+ direct_blockno = ext4fs_get_new_blk_no();
+ if (direct_blockno == -1) {
+ printf("no block left to assign\n");
+ return;
+ }
+ file_inode->b.blocks.dir_blocks[i] = cpu_to_le32(direct_blockno);
+ debug("DB %ld: %u\n", direct_blockno, total_remaining_blocks);
+
+ total_remaining_blocks--;
+ }
+
+ alloc_single_indirect_block(file_inode, &total_remaining_blocks,
+ &no_blks_reqd);
+ alloc_double_indirect_block(file_inode, &total_remaining_blocks,
+ &no_blks_reqd);
+ alloc_triple_indirect_block(file_inode, &total_remaining_blocks,
+ &no_blks_reqd);
+ *total_no_of_block += no_blks_reqd;
+}
+
+#endif
+
+static struct ext4_extent_header *ext4fs_get_extent_block
+ (struct ext2_data *data, struct ext_block_cache *cache,
+ struct ext4_extent_header *ext_block,
+ uint32_t fileblock, int log2_blksz)
+{
+ struct ext4_extent_idx *index;
+ unsigned long long block;
+ int blksz = EXT2_BLOCK_SIZE(data);
+ int i;
+
+ while (1) {
+ index = (struct ext4_extent_idx *)(ext_block + 1);
+
+ if (le16_to_cpu(ext_block->eh_magic) != EXT4_EXT_MAGIC)
+ return NULL;
+
+ if (ext_block->eh_depth == 0)
+ return ext_block;
+ i = -1;
+ do {
+ i++;
+ if (i >= le16_to_cpu(ext_block->eh_entries))
+ break;
+ } while (fileblock >= le32_to_cpu(index[i].ei_block));
+
+ /*
+ * If first logical block number is higher than requested fileblock,
+ * it is a sparse file. This is handled on upper layer.
+ */
+ if (i > 0)
+ i--;
+
+ block = le16_to_cpu(index[i].ei_leaf_hi);
+ block = (block << 32) + le32_to_cpu(index[i].ei_leaf_lo);
+ block <<= log2_blksz;
+ if (!ext_cache_read(cache, (lbaint_t)block, blksz))
+ return NULL;
+ ext_block = (struct ext4_extent_header *)cache->buf;
+ }
+}
+
+static int ext4fs_blockgroup
+ (struct ext2_data *data, int group, struct ext2_block_group *blkgrp)
+{
+ long int blkno;
+ unsigned int blkoff, desc_per_blk;
+ int log2blksz = get_fs()->dev_desc->log2blksz;
+ int desc_size = get_fs()->gdsize;
+
+ if (desc_size == 0)
+ return 0;
+ desc_per_blk = EXT2_BLOCK_SIZE(data) / desc_size;
+
+ if (desc_per_blk == 0)
+ return 0;
+ blkno = le32_to_cpu(data->sblock.first_data_block) + 1 +
+ group / desc_per_blk;
+ blkoff = (group % desc_per_blk) * desc_size;
+
+ debug("ext4fs read %d group descriptor (blkno %ld blkoff %u)\n",
+ group, blkno, blkoff);
+
+ return ext4fs_devread((lbaint_t)blkno <<
+ (LOG2_BLOCK_SIZE(data) - log2blksz),
+ blkoff, desc_size, (char *)blkgrp);
+}
+
+int ext4fs_read_inode(struct ext2_data *data, int ino, struct ext2_inode *inode)
+{
+ struct ext2_block_group *blkgrp;
+ struct ext2_sblock *sblock = &data->sblock;
+ struct ext_filesystem *fs = get_fs();
+ int log2blksz = get_fs()->dev_desc->log2blksz;
+ int inodes_per_block, status;
+ long int blkno;
+ unsigned int blkoff;
+
+ /* Allocate blkgrp based on gdsize (for 64-bit support). */
+ blkgrp = zalloc(get_fs()->gdsize);
+ if (!blkgrp)
+ return 0;
+
+ /* It is easier to calculate if the first inode is 0. */
+ ino--;
+ if ( le32_to_cpu(sblock->inodes_per_group) == 0 || fs->inodesz == 0) {
+ free(blkgrp);
+ return 0;
+ }
+ status = ext4fs_blockgroup(data, ino / le32_to_cpu
+ (sblock->inodes_per_group), blkgrp);
+ if (status == 0) {
+ free(blkgrp);
+ return 0;
+ }
+
+ inodes_per_block = EXT2_BLOCK_SIZE(data) / fs->inodesz;
+ if ( inodes_per_block == 0 ) {
+ free(blkgrp);
+ return 0;
+ }
+ blkno = ext4fs_bg_get_inode_table_id(blkgrp, fs) +
+ (ino % le32_to_cpu(sblock->inodes_per_group)) / inodes_per_block;
+ blkoff = (ino % inodes_per_block) * fs->inodesz;
+
+ /* Free blkgrp as it is no longer required. */
+ free(blkgrp);
+
+ /* Read the inode. */
+ status = ext4fs_devread((lbaint_t)blkno << (LOG2_BLOCK_SIZE(data) -
+ log2blksz), blkoff,
+ sizeof(struct ext2_inode), (char *)inode);
+ if (status == 0)
+ return 0;
+
+ return 1;
+}
+
+long int read_allocated_block(struct ext2_inode *inode, int fileblock,
+ struct ext_block_cache *cache)
+{
+ long int blknr;
+ int blksz;
+ int log2_blksz;
+ int status;
+ long int rblock;
+ long int perblock_parent;
+ long int perblock_child;
+ unsigned long long start;
+ /* get the blocksize of the filesystem */
+ blksz = EXT2_BLOCK_SIZE(ext4fs_root);
+ log2_blksz = LOG2_BLOCK_SIZE(ext4fs_root)
+ - get_fs()->dev_desc->log2blksz;
+
+ if (le32_to_cpu(inode->flags) & EXT4_EXTENTS_FL) {
+ long int startblock, endblock;
+ struct ext_block_cache *c, cd;
+ struct ext4_extent_header *ext_block;
+ struct ext4_extent *extent;
+ int i;
+
+ if (cache) {
+ c = cache;
+ } else {
+ c = &cd;
+ ext_cache_init(c);
+ }
+ ext_block =
+ ext4fs_get_extent_block(ext4fs_root, c,
+ (struct ext4_extent_header *)
+ inode->b.blocks.dir_blocks,
+ fileblock, log2_blksz);
+ if (!ext_block) {
+ printf("invalid extent block\n");
+ if (!cache)
+ ext_cache_fini(c);
+ return -EINVAL;
+ }
+
+ extent = (struct ext4_extent *)(ext_block + 1);
+
+ for (i = 0; i < le16_to_cpu(ext_block->eh_entries); i++) {
+ startblock = le32_to_cpu(extent[i].ee_block);
+ endblock = startblock + le16_to_cpu(extent[i].ee_len);
+
+ if (startblock > fileblock) {
+ /* Sparse file */
+ if (!cache)
+ ext_cache_fini(c);
+ return 0;
+
+ } else if (fileblock < endblock) {
+ start = le16_to_cpu(extent[i].ee_start_hi);
+ start = (start << 32) +
+ le32_to_cpu(extent[i].ee_start_lo);
+ if (!cache)
+ ext_cache_fini(c);
+ return (fileblock - startblock) + start;
+ }
+ }
+
+ if (!cache)
+ ext_cache_fini(c);
+ return 0;
+ }
+
+ /* Direct blocks. */
+ if (fileblock < INDIRECT_BLOCKS)
+ blknr = le32_to_cpu(inode->b.blocks.dir_blocks[fileblock]);
+
+ /* Indirect. */
+ else if (fileblock < (INDIRECT_BLOCKS + (blksz / 4))) {
+ if (ext4fs_indir1_block == NULL) {
+ ext4fs_indir1_block = zalloc(blksz);
+ if (ext4fs_indir1_block == NULL) {
+ printf("** SI ext2fs read block (indir 1)"
+ "malloc failed. **\n");
+ return -1;
+ }
+ ext4fs_indir1_size = blksz;
+ ext4fs_indir1_blkno = -1;
+ }
+ if (blksz != ext4fs_indir1_size) {
+ free(ext4fs_indir1_block);
+ ext4fs_indir1_block = NULL;
+ ext4fs_indir1_size = 0;
+ ext4fs_indir1_blkno = -1;
+ ext4fs_indir1_block = zalloc(blksz);
+ if (ext4fs_indir1_block == NULL) {
+ printf("** SI ext2fs read block (indir 1):"
+ "malloc failed. **\n");
+ return -1;
+ }
+ ext4fs_indir1_size = blksz;
+ }
+ if ((le32_to_cpu(inode->b.blocks.indir_block) <<
+ log2_blksz) != ext4fs_indir1_blkno) {
+ status =
+ ext4fs_devread((lbaint_t)le32_to_cpu
+ (inode->b.blocks.
+ indir_block) << log2_blksz, 0,
+ blksz, (char *)ext4fs_indir1_block);
+ if (status == 0) {
+ printf("** SI ext2fs read block (indir 1)"
+ "failed. **\n");
+ return -1;
+ }
+ ext4fs_indir1_blkno =
+ le32_to_cpu(inode->b.blocks.
+ indir_block) << log2_blksz;
+ }
+ blknr = le32_to_cpu(ext4fs_indir1_block
+ [fileblock - INDIRECT_BLOCKS]);
+ }
+ /* Double indirect. */
+ else if (fileblock < (INDIRECT_BLOCKS + (blksz / 4 *
+ (blksz / 4 + 1)))) {
+
+ long int perblock = blksz / 4;
+ long int rblock = fileblock - (INDIRECT_BLOCKS + blksz / 4);
+
+ if (ext4fs_indir1_block == NULL) {
+ ext4fs_indir1_block = zalloc(blksz);
+ if (ext4fs_indir1_block == NULL) {
+ printf("** DI ext2fs read block (indir 2 1)"
+ "malloc failed. **\n");
+ return -1;
+ }
+ ext4fs_indir1_size = blksz;
+ ext4fs_indir1_blkno = -1;
+ }
+ if (blksz != ext4fs_indir1_size) {
+ free(ext4fs_indir1_block);
+ ext4fs_indir1_block = NULL;
+ ext4fs_indir1_size = 0;
+ ext4fs_indir1_blkno = -1;
+ ext4fs_indir1_block = zalloc(blksz);
+ if (ext4fs_indir1_block == NULL) {
+ printf("** DI ext2fs read block (indir 2 1)"
+ "malloc failed. **\n");
+ return -1;
+ }
+ ext4fs_indir1_size = blksz;
+ }
+ if ((le32_to_cpu(inode->b.blocks.double_indir_block) <<
+ log2_blksz) != ext4fs_indir1_blkno) {
+ status =
+ ext4fs_devread((lbaint_t)le32_to_cpu
+ (inode->b.blocks.
+ double_indir_block) << log2_blksz,
+ 0, blksz,
+ (char *)ext4fs_indir1_block);
+ if (status == 0) {
+ printf("** DI ext2fs read block (indir 2 1)"
+ "failed. **\n");
+ return -1;
+ }
+ ext4fs_indir1_blkno =
+ le32_to_cpu(inode->b.blocks.double_indir_block) <<
+ log2_blksz;
+ }
+
+ if (ext4fs_indir2_block == NULL) {
+ ext4fs_indir2_block = zalloc(blksz);
+ if (ext4fs_indir2_block == NULL) {
+ printf("** DI ext2fs read block (indir 2 2)"
+ "malloc failed. **\n");
+ return -1;
+ }
+ ext4fs_indir2_size = blksz;
+ ext4fs_indir2_blkno = -1;
+ }
+ if (blksz != ext4fs_indir2_size) {
+ free(ext4fs_indir2_block);
+ ext4fs_indir2_block = NULL;
+ ext4fs_indir2_size = 0;
+ ext4fs_indir2_blkno = -1;
+ ext4fs_indir2_block = zalloc(blksz);
+ if (ext4fs_indir2_block == NULL) {
+ printf("** DI ext2fs read block (indir 2 2)"
+ "malloc failed. **\n");
+ return -1;
+ }
+ ext4fs_indir2_size = blksz;
+ }
+ if ((le32_to_cpu(ext4fs_indir1_block[rblock / perblock]) <<
+ log2_blksz) != ext4fs_indir2_blkno) {
+ status = ext4fs_devread((lbaint_t)le32_to_cpu
+ (ext4fs_indir1_block
+ [rblock /
+ perblock]) << log2_blksz, 0,
+ blksz,
+ (char *)ext4fs_indir2_block);
+ if (status == 0) {
+ printf("** DI ext2fs read block (indir 2 2)"
+ "failed. **\n");
+ return -1;
+ }
+ ext4fs_indir2_blkno =
+ le32_to_cpu(ext4fs_indir1_block[rblock
+ /
+ perblock]) <<
+ log2_blksz;
+ }
+ blknr = le32_to_cpu(ext4fs_indir2_block[rblock % perblock]);
+ }
+ /* Tripple indirect. */
+ else {
+ rblock = fileblock - (INDIRECT_BLOCKS + blksz / 4 +
+ (blksz / 4 * blksz / 4));
+ perblock_child = blksz / 4;
+ perblock_parent = ((blksz / 4) * (blksz / 4));
+
+ if (ext4fs_indir1_block == NULL) {
+ ext4fs_indir1_block = zalloc(blksz);
+ if (ext4fs_indir1_block == NULL) {
+ printf("** TI ext2fs read block (indir 2 1)"
+ "malloc failed. **\n");
+ return -1;
+ }
+ ext4fs_indir1_size = blksz;
+ ext4fs_indir1_blkno = -1;
+ }
+ if (blksz != ext4fs_indir1_size) {
+ free(ext4fs_indir1_block);
+ ext4fs_indir1_block = NULL;
+ ext4fs_indir1_size = 0;
+ ext4fs_indir1_blkno = -1;
+ ext4fs_indir1_block = zalloc(blksz);
+ if (ext4fs_indir1_block == NULL) {
+ printf("** TI ext2fs read block (indir 2 1)"
+ "malloc failed. **\n");
+ return -1;
+ }
+ ext4fs_indir1_size = blksz;
+ }
+ if ((le32_to_cpu(inode->b.blocks.triple_indir_block) <<
+ log2_blksz) != ext4fs_indir1_blkno) {
+ status = ext4fs_devread
+ ((lbaint_t)
+ le32_to_cpu(inode->b.blocks.triple_indir_block)
+ << log2_blksz, 0, blksz,
+ (char *)ext4fs_indir1_block);
+ if (status == 0) {
+ printf("** TI ext2fs read block (indir 2 1)"
+ "failed. **\n");
+ return -1;
+ }
+ ext4fs_indir1_blkno =
+ le32_to_cpu(inode->b.blocks.triple_indir_block) <<
+ log2_blksz;
+ }
+
+ if (ext4fs_indir2_block == NULL) {
+ ext4fs_indir2_block = zalloc(blksz);
+ if (ext4fs_indir2_block == NULL) {
+ printf("** TI ext2fs read block (indir 2 2)"
+ "malloc failed. **\n");
+ return -1;
+ }
+ ext4fs_indir2_size = blksz;
+ ext4fs_indir2_blkno = -1;
+ }
+ if (blksz != ext4fs_indir2_size) {
+ free(ext4fs_indir2_block);
+ ext4fs_indir2_block = NULL;
+ ext4fs_indir2_size = 0;
+ ext4fs_indir2_blkno = -1;
+ ext4fs_indir2_block = zalloc(blksz);
+ if (ext4fs_indir2_block == NULL) {
+ printf("** TI ext2fs read block (indir 2 2)"
+ "malloc failed. **\n");
+ return -1;
+ }
+ ext4fs_indir2_size = blksz;
+ }
+ if ((le32_to_cpu(ext4fs_indir1_block[rblock /
+ perblock_parent]) <<
+ log2_blksz)
+ != ext4fs_indir2_blkno) {
+ status = ext4fs_devread((lbaint_t)le32_to_cpu
+ (ext4fs_indir1_block
+ [rblock /
+ perblock_parent]) <<
+ log2_blksz, 0, blksz,
+ (char *)ext4fs_indir2_block);
+ if (status == 0) {
+ printf("** TI ext2fs read block (indir 2 2)"
+ "failed. **\n");
+ return -1;
+ }
+ ext4fs_indir2_blkno =
+ le32_to_cpu(ext4fs_indir1_block[rblock /
+ perblock_parent])
+ << log2_blksz;
+ }
+
+ if (ext4fs_indir3_block == NULL) {
+ ext4fs_indir3_block = zalloc(blksz);
+ if (ext4fs_indir3_block == NULL) {
+ printf("** TI ext2fs read block (indir 2 2)"
+ "malloc failed. **\n");
+ return -1;
+ }
+ ext4fs_indir3_size = blksz;
+ ext4fs_indir3_blkno = -1;
+ }
+ if (blksz != ext4fs_indir3_size) {
+ free(ext4fs_indir3_block);
+ ext4fs_indir3_block = NULL;
+ ext4fs_indir3_size = 0;
+ ext4fs_indir3_blkno = -1;
+ ext4fs_indir3_block = zalloc(blksz);
+ if (ext4fs_indir3_block == NULL) {
+ printf("** TI ext2fs read block (indir 2 2)"
+ "malloc failed. **\n");
+ return -1;
+ }
+ ext4fs_indir3_size = blksz;
+ }
+ if ((le32_to_cpu(ext4fs_indir2_block[rblock
+ /
+ perblock_child]) <<
+ log2_blksz) != ext4fs_indir3_blkno) {
+ status =
+ ext4fs_devread((lbaint_t)le32_to_cpu
+ (ext4fs_indir2_block
+ [(rblock / perblock_child)
+ % (blksz / 4)]) << log2_blksz, 0,
+ blksz, (char *)ext4fs_indir3_block);
+ if (status == 0) {
+ printf("** TI ext2fs read block (indir 2 2)"
+ "failed. **\n");
+ return -1;
+ }
+ ext4fs_indir3_blkno =
+ le32_to_cpu(ext4fs_indir2_block[(rblock /
+ perblock_child) %
+ (blksz /
+ 4)]) <<
+ log2_blksz;
+ }
+
+ blknr = le32_to_cpu(ext4fs_indir3_block
+ [rblock % perblock_child]);
+ }
+ debug("read_allocated_block %ld\n", blknr);
+
+ return blknr;
+}
+
+/**
+ * ext4fs_reinit_global() - Reinitialize values of ext4 write implementation's
+ * global pointers
+ *
+ * This function assures that for a file with the same name but different size
+ * the sequential store on the ext4 filesystem will be correct.
+ *
+ * In this function the global data, responsible for internal representation
+ * of the ext4 data are initialized to the reset state. Without this, during
+ * replacement of the smaller file with the bigger truncation of new file was
+ * performed.
+ */
+void ext4fs_reinit_global(void)
+{
+ if (ext4fs_indir1_block != NULL) {
+ free(ext4fs_indir1_block);
+ ext4fs_indir1_block = NULL;
+ ext4fs_indir1_size = 0;
+ ext4fs_indir1_blkno = -1;
+ }
+ if (ext4fs_indir2_block != NULL) {
+ free(ext4fs_indir2_block);
+ ext4fs_indir2_block = NULL;
+ ext4fs_indir2_size = 0;
+ ext4fs_indir2_blkno = -1;
+ }
+ if (ext4fs_indir3_block != NULL) {
+ free(ext4fs_indir3_block);
+ ext4fs_indir3_block = NULL;
+ ext4fs_indir3_size = 0;
+ ext4fs_indir3_blkno = -1;
+ }
+}
+void ext4fs_close(void)
+{
+ if ((ext4fs_file != NULL) && (ext4fs_root != NULL)) {
+ ext4fs_free_node(ext4fs_file, &ext4fs_root->diropen);
+ ext4fs_file = NULL;
+ }
+ if (ext4fs_root != NULL) {
+ free(ext4fs_root);
+ ext4fs_root = NULL;
+ }
+
+ ext4fs_reinit_global();
+}
+
+int ext4fs_iterate_dir(struct ext2fs_node *dir, char *name,
+ struct ext2fs_node **fnode, int *ftype)
+{
+ unsigned int fpos = 0;
+ int status;
+ loff_t actread;
+ struct ext2fs_node *diro = (struct ext2fs_node *) dir;
+
+#ifdef DEBUG
+ if (name != NULL)
+ printf("Iterate dir %s\n", name);
+#endif /* of DEBUG */
+ if (!diro->inode_read) {
+ status = ext4fs_read_inode(diro->data, diro->ino, &diro->inode);
+ if (status == 0)
+ return 0;
+ }
+ /* Search the file. */
+ while (fpos < le32_to_cpu(diro->inode.size)) {
+ struct ext2_dirent dirent;
+
+ status = ext4fs_read_file(diro, fpos,
+ sizeof(struct ext2_dirent),
+ (char *)&dirent, &actread);
+ if (status < 0)
+ return 0;
+
+ if (dirent.direntlen == 0) {
+ printf("Failed to iterate over directory %s\n", name);
+ return 0;
+ }
+
+ if (dirent.namelen != 0) {
+ char filename[dirent.namelen + 1];
+ struct ext2fs_node *fdiro;
+ int type = FILETYPE_UNKNOWN;
+
+ status = ext4fs_read_file(diro,
+ fpos +
+ sizeof(struct ext2_dirent),
+ dirent.namelen, filename,
+ &actread);
+ if (status < 0)
+ return 0;
+
+ fdiro = zalloc(sizeof(struct ext2fs_node));
+ if (!fdiro)
+ return 0;
+
+ fdiro->data = diro->data;
+ fdiro->ino = le32_to_cpu(dirent.inode);
+
+ filename[dirent.namelen] = '\0';
+
+ if (dirent.filetype != FILETYPE_UNKNOWN) {
+ fdiro->inode_read = 0;
+
+ if (dirent.filetype == FILETYPE_DIRECTORY)
+ type = FILETYPE_DIRECTORY;
+ else if (dirent.filetype == FILETYPE_SYMLINK)
+ type = FILETYPE_SYMLINK;
+ else if (dirent.filetype == FILETYPE_REG)
+ type = FILETYPE_REG;
+ } else {
+ status = ext4fs_read_inode(diro->data,
+ le32_to_cpu
+ (dirent.inode),
+ &fdiro->inode);
+ if (status == 0) {
+ free(fdiro);
+ return 0;
+ }
+ fdiro->inode_read = 1;
+
+ if ((le16_to_cpu(fdiro->inode.mode) &
+ FILETYPE_INO_MASK) ==
+ FILETYPE_INO_DIRECTORY) {
+ type = FILETYPE_DIRECTORY;
+ } else if ((le16_to_cpu(fdiro->inode.mode)
+ & FILETYPE_INO_MASK) ==
+ FILETYPE_INO_SYMLINK) {
+ type = FILETYPE_SYMLINK;
+ } else if ((le16_to_cpu(fdiro->inode.mode)
+ & FILETYPE_INO_MASK) ==
+ FILETYPE_INO_REG) {
+ type = FILETYPE_REG;
+ }
+ }
+#ifdef DEBUG
+ printf("iterate >%s<\n", filename);
+#endif /* of DEBUG */
+ if ((name != NULL) && (fnode != NULL)
+ && (ftype != NULL)) {
+ if (strcmp(filename, name) == 0) {
+ *ftype = type;
+ *fnode = fdiro;
+ return 1;
+ }
+ } else {
+ if (fdiro->inode_read == 0) {
+ status = ext4fs_read_inode(diro->data,
+ le32_to_cpu(
+ dirent.inode),
+ &fdiro->inode);
+ if (status == 0) {
+ free(fdiro);
+ return 0;
+ }
+ fdiro->inode_read = 1;
+ }
+ switch (type) {
+ case FILETYPE_DIRECTORY:
+ printf("<DIR> ");
+ break;
+ case FILETYPE_SYMLINK:
+ printf("<SYM> ");
+ break;
+ case FILETYPE_REG:
+ printf(" ");
+ break;
+ default:
+ printf("< ? > ");
+ break;
+ }
+ printf("%10u %s\n",
+ le32_to_cpu(fdiro->inode.size),
+ filename);
+ }
+ free(fdiro);
+ }
+ fpos += le16_to_cpu(dirent.direntlen);
+ }
+ return 0;
+}
+
+static char *ext4fs_read_symlink(struct ext2fs_node *node)
+{
+ char *symlink;
+ struct ext2fs_node *diro = node;
+ int status;
+ loff_t actread;
+
+ if (!diro->inode_read) {
+ status = ext4fs_read_inode(diro->data, diro->ino, &diro->inode);
+ if (status == 0)
+ return NULL;
+ }
+ symlink = zalloc(le32_to_cpu(diro->inode.size) + 1);
+ if (!symlink)
+ return NULL;
+
+ if (le32_to_cpu(diro->inode.size) < sizeof(diro->inode.b.symlink)) {
+ strncpy(symlink, diro->inode.b.symlink,
+ le32_to_cpu(diro->inode.size));
+ } else {
+ status = ext4fs_read_file(diro, 0,
+ le32_to_cpu(diro->inode.size),
+ symlink, &actread);
+ if ((status < 0) || (actread == 0)) {
+ free(symlink);
+ return NULL;
+ }
+ }
+ symlink[le32_to_cpu(diro->inode.size)] = '\0';
+ return symlink;
+}
+
+static int ext4fs_find_file1(const char *currpath,
+ struct ext2fs_node *currroot,
+ struct ext2fs_node **currfound, int *foundtype)
+{
+ char fpath[strlen(currpath) + 1];
+ char *name = fpath;
+ char *next;
+ int status;
+ int type = FILETYPE_DIRECTORY;
+ struct ext2fs_node *currnode = currroot;
+ struct ext2fs_node *oldnode = currroot;
+
+ strncpy(fpath, currpath, strlen(currpath) + 1);
+
+ /* Remove all leading slashes. */
+ while (*name == '/')
+ name++;
+
+ if (!*name) {
+ *currfound = currnode;
+ return 1;
+ }
+
+ for (;;) {
+ int found;
+
+ /* Extract the actual part from the pathname. */
+ next = strchr(name, '/');
+ if (next) {
+ /* Remove all leading slashes. */
+ while (*next == '/')
+ *(next++) = '\0';
+ }
+
+ if (type != FILETYPE_DIRECTORY) {
+ ext4fs_free_node(currnode, currroot);
+ return 0;
+ }
+
+ oldnode = currnode;
+
+ /* Iterate over the directory. */
+ found = ext4fs_iterate_dir(currnode, name, &currnode, &type);
+ if (found == 0)
+ return 0;
+
+ if (found == -1)
+ break;
+
+ /* Read in the symlink and follow it. */
+ if (type == FILETYPE_SYMLINK) {
+ char *symlink;
+
+ /* Test if the symlink does not loop. */
+ if (++symlinknest == 8) {
+ ext4fs_free_node(currnode, currroot);
+ ext4fs_free_node(oldnode, currroot);
+ return 0;
+ }
+
+ symlink = ext4fs_read_symlink(currnode);
+ ext4fs_free_node(currnode, currroot);
+
+ if (!symlink) {
+ ext4fs_free_node(oldnode, currroot);
+ return 0;
+ }
+
+ debug("Got symlink >%s<\n", symlink);
+
+ if (symlink[0] == '/') {
+ ext4fs_free_node(oldnode, currroot);
+ oldnode = &ext4fs_root->diropen;
+ }
+
+ /* Lookup the node the symlink points to. */
+ status = ext4fs_find_file1(symlink, oldnode,
+ &currnode, &type);
+
+ free(symlink);
+
+ if (status == 0) {
+ ext4fs_free_node(oldnode, currroot);
+ return 0;
+ }
+ }
+
+ ext4fs_free_node(oldnode, currroot);
+
+ /* Found the node! */
+ if (!next || *next == '\0') {
+ *currfound = currnode;
+ *foundtype = type;
+ return 1;
+ }
+ name = next;
+ }
+ return -1;
+}
+
+int ext4fs_find_file(const char *path, struct ext2fs_node *rootnode,
+ struct ext2fs_node **foundnode, int expecttype)
+{
+ int status;
+ int foundtype = FILETYPE_DIRECTORY;
+
+ symlinknest = 0;
+ if (!path)
+ return 0;
+
+ status = ext4fs_find_file1(path, rootnode, foundnode, &foundtype);
+ if (status == 0)
+ return 0;
+
+ /* Check if the node that was found was of the expected type. */
+ if ((expecttype == FILETYPE_REG) && (foundtype != expecttype))
+ return 0;
+ else if ((expecttype == FILETYPE_DIRECTORY)
+ && (foundtype != expecttype))
+ return 0;
+
+ return 1;
+}
+
+int ext4fs_open(const char *filename, loff_t *len)
+{
+ struct ext2fs_node *fdiro = NULL;
+ int status;
+
+ if (ext4fs_root == NULL)
+ return -1;
+
+ ext4fs_file = NULL;
+ status = ext4fs_find_file(filename, &ext4fs_root->diropen, &fdiro,
+ FILETYPE_REG);
+ if (status == 0)
+ goto fail;
+
+ if (!fdiro->inode_read) {
+ status = ext4fs_read_inode(fdiro->data, fdiro->ino,
+ &fdiro->inode);
+ if (status == 0)
+ goto fail;
+ }
+ *len = le32_to_cpu(fdiro->inode.size);
+ ext4fs_file = fdiro;
+
+ return 0;
+fail:
+ ext4fs_free_node(fdiro, &ext4fs_root->diropen);
+
+ return -1;
+}
+
+int ext4fs_mount(unsigned part_length)
+{
+ struct ext2_data *data;
+ int status;
+ struct ext_filesystem *fs = get_fs();
+ data = zalloc(SUPERBLOCK_SIZE);
+ if (!data)
+ return 0;
+
+ /* Read the superblock. */
+ status = ext4_read_superblock((char *)&data->sblock);
+
+ if (status == 0)
+ goto fail;
+
+ /* Make sure this is an ext2 filesystem. */
+ if (le16_to_cpu(data->sblock.magic) != EXT2_MAGIC)
+ goto fail_noerr;
+
+
+ if (le32_to_cpu(data->sblock.revision_level) == 0) {
+ fs->inodesz = 128;
+ fs->gdsize = 32;
+ } else {
+ debug("EXT4 features COMPAT: %08x INCOMPAT: %08x RO_COMPAT: %08x\n",
+ __le32_to_cpu(data->sblock.feature_compatibility),
+ __le32_to_cpu(data->sblock.feature_incompat),
+ __le32_to_cpu(data->sblock.feature_ro_compat));
+
+ fs->inodesz = le16_to_cpu(data->sblock.inode_size);
+ fs->gdsize = le32_to_cpu(data->sblock.feature_incompat) &
+ EXT4_FEATURE_INCOMPAT_64BIT ?
+ le16_to_cpu(data->sblock.descriptor_size) : 32;
+ }
+
+ debug("EXT2 rev %d, inode_size %d, descriptor size %d\n",
+ le32_to_cpu(data->sblock.revision_level),
+ fs->inodesz, fs->gdsize);
+
+ data->diropen.data = data;
+ data->diropen.ino = 2;
+ data->diropen.inode_read = 1;
+ data->inode = &data->diropen.inode;
+
+ status = ext4fs_read_inode(data, 2, data->inode);
+ if (status == 0)
+ goto fail;
+
+ ext4fs_root = data;
+
+ return 1;
+fail:
+ printf("Failed to mount ext2 filesystem...\n");
+fail_noerr:
+ free(data);
+ ext4fs_root = NULL;
+
+ return 0;
+}
diff --git a/roms/u-boot/fs/ext4/ext4_common.h b/roms/u-boot/fs/ext4/ext4_common.h
new file mode 100644
index 000000000..beaee9c80
--- /dev/null
+++ b/roms/u-boot/fs/ext4/ext4_common.h
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * (C) Copyright 2011 - 2012 Samsung Electronics
+ * EXT4 filesystem implementation in Uboot by
+ * Uma Shankar <uma.shankar@samsung.com>
+ * Manjunatha C Achar <a.manjunatha@samsung.com>
+ *
+ * ext4ls and ext4load : based on ext2 ls load support in Uboot.
+ *
+ * (C) Copyright 2004
+ * esd gmbh <www.esd-electronics.com>
+ * Reinhard Arlt <reinhard.arlt@esd-electronics.com>
+ *
+ * based on code from grub2 fs/ext2.c and fs/fshelp.c by
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+ *
+ * ext4write : Based on generic ext4 protocol.
+ */
+
+#ifndef __EXT4_COMMON__
+#define __EXT4_COMMON__
+#include <ext_common.h>
+#include <ext4fs.h>
+#include <malloc.h>
+#include <asm/cache.h>
+#include <linux/errno.h>
+#if defined(CONFIG_EXT4_WRITE)
+#include "ext4_journal.h"
+#include "crc16.h"
+#endif
+
+#define YES 1
+#define NO 0
+#define RECOVER 1
+#define SCAN 0
+
+#define S_IFLNK 0120000 /* symbolic link */
+#define BLOCK_NO_ONE 1
+#define SUPERBLOCK_START (2 * 512)
+#define SUPERBLOCK_SIZE 1024
+#define F_FILE 1
+
+static inline void *zalloc(size_t size)
+{
+ void *p = memalign(ARCH_DMA_MINALIGN, size);
+ memset(p, 0, size);
+ return p;
+}
+
+int ext4fs_read_inode(struct ext2_data *data, int ino,
+ struct ext2_inode *inode);
+int ext4fs_read_file(struct ext2fs_node *node, loff_t pos, loff_t len,
+ char *buf, loff_t *actread);
+int ext4fs_find_file(const char *path, struct ext2fs_node *rootnode,
+ struct ext2fs_node **foundnode, int expecttype);
+int ext4fs_iterate_dir(struct ext2fs_node *dir, char *name,
+ struct ext2fs_node **fnode, int *ftype);
+
+#if defined(CONFIG_EXT4_WRITE)
+uint32_t ext4fs_div_roundup(uint32_t size, uint32_t n);
+uint16_t ext4fs_checksum_update(unsigned int i);
+int ext4fs_get_parent_inode_num(const char *dirname, char *dname, int flags);
+int ext4fs_update_parent_dentry(char *filename, int file_type);
+uint32_t ext4fs_get_new_blk_no(void);
+int ext4fs_get_new_inode_no(void);
+void ext4fs_reset_block_bmap(long int blockno, unsigned char *buffer,
+ int index);
+int ext4fs_set_block_bmap(long int blockno, unsigned char *buffer, int index);
+int ext4fs_set_inode_bmap(int inode_no, unsigned char *buffer, int index);
+void ext4fs_reset_inode_bmap(int inode_no, unsigned char *buffer, int index);
+int ext4fs_iget(int inode_no, struct ext2_inode *inode);
+void ext4fs_allocate_blocks(struct ext2_inode *file_inode,
+ unsigned int total_remaining_blocks,
+ unsigned int *total_no_of_block);
+void put_ext4(uint64_t off, const void *buf, uint32_t size);
+struct ext2_block_group *ext4fs_get_group_descriptor
+ (const struct ext_filesystem *fs, uint32_t bg_idx);
+uint64_t ext4fs_bg_get_block_id(const struct ext2_block_group *bg,
+ const struct ext_filesystem *fs);
+uint64_t ext4fs_bg_get_inode_id(const struct ext2_block_group *bg,
+ const struct ext_filesystem *fs);
+uint64_t ext4fs_bg_get_inode_table_id(const struct ext2_block_group *bg,
+ const struct ext_filesystem *fs);
+uint64_t ext4fs_sb_get_free_blocks(const struct ext2_sblock *sb);
+void ext4fs_sb_set_free_blocks(struct ext2_sblock *sb, uint64_t free_blocks);
+uint32_t ext4fs_bg_get_free_blocks(const struct ext2_block_group *bg,
+ const struct ext_filesystem *fs);
+#endif
+#endif
diff --git a/roms/u-boot/fs/ext4/ext4_journal.c b/roms/u-boot/fs/ext4/ext4_journal.c
new file mode 100644
index 000000000..1a340b476
--- /dev/null
+++ b/roms/u-boot/fs/ext4/ext4_journal.c
@@ -0,0 +1,666 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2011 - 2012 Samsung Electronics
+ * EXT4 filesystem implementation in Uboot by
+ * Uma Shankar <uma.shankar@samsung.com>
+ * Manjunatha C Achar <a.manjunatha@samsung.com>
+ *
+ * Journal data structures and headers for Journaling feature of ext4
+ * have been referred from JBD2 (Journaling Block device 2)
+ * implementation in Linux Kernel.
+ * Written by Stephen C. Tweedie <sct@redhat.com>
+ *
+ * Copyright 1998-2000 Red Hat, Inc --- All Rights Reserved
+ */
+
+#include <common.h>
+#include <blk.h>
+#include <ext4fs.h>
+#include <log.h>
+#include <malloc.h>
+#include <ext_common.h>
+#include "ext4_common.h"
+
+static struct revoke_blk_list *revk_blk_list;
+static struct revoke_blk_list *prev_node;
+static int first_node = true;
+
+int gindex;
+int gd_index;
+int jrnl_blk_idx;
+struct journal_log *journal_ptr[MAX_JOURNAL_ENTRIES];
+struct dirty_blocks *dirty_block_ptr[MAX_JOURNAL_ENTRIES];
+
+int ext4fs_init_journal(void)
+{
+ int i;
+ char *temp = NULL;
+ struct ext_filesystem *fs = get_fs();
+
+ /* init globals */
+ revk_blk_list = NULL;
+ prev_node = NULL;
+ gindex = 0;
+ gd_index = 0;
+ jrnl_blk_idx = 1;
+
+ for (i = 0; i < MAX_JOURNAL_ENTRIES; i++) {
+ journal_ptr[i] = zalloc(sizeof(struct journal_log));
+ if (!journal_ptr[i])
+ goto fail;
+ dirty_block_ptr[i] = zalloc(sizeof(struct dirty_blocks));
+ if (!dirty_block_ptr[i])
+ goto fail;
+ journal_ptr[i]->buf = NULL;
+ journal_ptr[i]->blknr = -1;
+
+ dirty_block_ptr[i]->buf = NULL;
+ dirty_block_ptr[i]->blknr = -1;
+ }
+
+ if (fs->blksz == 4096) {
+ temp = zalloc(fs->blksz);
+ if (!temp)
+ goto fail;
+ journal_ptr[gindex]->buf = zalloc(fs->blksz);
+ if (!journal_ptr[gindex]->buf)
+ goto fail;
+ ext4fs_devread(0, 0, fs->blksz, temp);
+ memcpy(temp + SUPERBLOCK_SIZE, fs->sb, SUPERBLOCK_SIZE);
+ memcpy(journal_ptr[gindex]->buf, temp, fs->blksz);
+ journal_ptr[gindex++]->blknr = 0;
+ free(temp);
+ } else {
+ journal_ptr[gindex]->buf = zalloc(fs->blksz);
+ if (!journal_ptr[gindex]->buf)
+ goto fail;
+ memcpy(journal_ptr[gindex]->buf, fs->sb, SUPERBLOCK_SIZE);
+ journal_ptr[gindex++]->blknr = 1;
+ }
+
+ /* Check the file system state using journal super block */
+ if (ext4fs_check_journal_state(SCAN))
+ goto fail;
+ /* Check the file system state using journal super block */
+ if (ext4fs_check_journal_state(RECOVER))
+ goto fail;
+
+ return 0;
+fail:
+ return -1;
+}
+
+void ext4fs_dump_metadata(void)
+{
+ struct ext_filesystem *fs = get_fs();
+ int i;
+ for (i = 0; i < MAX_JOURNAL_ENTRIES; i++) {
+ if (dirty_block_ptr[i]->blknr == -1)
+ break;
+ put_ext4((uint64_t) ((uint64_t)dirty_block_ptr[i]->blknr *
+ (uint64_t)fs->blksz), dirty_block_ptr[i]->buf,
+ fs->blksz);
+ }
+}
+
+void ext4fs_free_journal(void)
+{
+ int i;
+ for (i = 0; i < MAX_JOURNAL_ENTRIES; i++) {
+ if (dirty_block_ptr[i]->blknr == -1)
+ break;
+ free(dirty_block_ptr[i]->buf);
+ }
+
+ for (i = 0; i < MAX_JOURNAL_ENTRIES; i++) {
+ if (journal_ptr[i]->blknr == -1)
+ break;
+ free(journal_ptr[i]->buf);
+ }
+
+ for (i = 0; i < MAX_JOURNAL_ENTRIES; i++) {
+ free(journal_ptr[i]);
+ free(dirty_block_ptr[i]);
+ }
+ gindex = 0;
+ gd_index = 0;
+ jrnl_blk_idx = 1;
+}
+
+int ext4fs_log_gdt(char *gd_table)
+{
+ struct ext_filesystem *fs = get_fs();
+ short i;
+ long int var = fs->gdtable_blkno;
+ for (i = 0; i < fs->no_blk_pergdt; i++) {
+ journal_ptr[gindex]->buf = zalloc(fs->blksz);
+ if (!journal_ptr[gindex]->buf)
+ return -ENOMEM;
+ memcpy(journal_ptr[gindex]->buf, gd_table, fs->blksz);
+ gd_table += fs->blksz;
+ journal_ptr[gindex++]->blknr = var++;
+ }
+
+ return 0;
+}
+
+/*
+ * This function stores the backup copy of meta data in RAM
+ * journal_buffer -- Buffer containing meta data
+ * blknr -- Block number on disk of the meta data buffer
+ */
+int ext4fs_log_journal(char *journal_buffer, uint32_t blknr)
+{
+ struct ext_filesystem *fs = get_fs();
+ short i;
+
+ if (!journal_buffer) {
+ printf("Invalid input arguments %s\n", __func__);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < MAX_JOURNAL_ENTRIES; i++) {
+ if (journal_ptr[i]->blknr == -1)
+ break;
+ if (journal_ptr[i]->blknr == blknr)
+ return 0;
+ }
+
+ journal_ptr[gindex]->buf = zalloc(fs->blksz);
+ if (!journal_ptr[gindex]->buf)
+ return -ENOMEM;
+
+ memcpy(journal_ptr[gindex]->buf, journal_buffer, fs->blksz);
+ journal_ptr[gindex++]->blknr = blknr;
+
+ return 0;
+}
+
+/*
+ * This function stores the modified meta data in RAM
+ * metadata_buffer -- Buffer containing meta data
+ * blknr -- Block number on disk of the meta data buffer
+ */
+int ext4fs_put_metadata(char *metadata_buffer, uint32_t blknr)
+{
+ struct ext_filesystem *fs = get_fs();
+ if (!metadata_buffer) {
+ printf("Invalid input arguments %s\n", __func__);
+ return -EINVAL;
+ }
+ if (dirty_block_ptr[gd_index]->buf)
+ assert(dirty_block_ptr[gd_index]->blknr == blknr);
+ else
+ dirty_block_ptr[gd_index]->buf = zalloc(fs->blksz);
+
+ if (!dirty_block_ptr[gd_index]->buf)
+ return -ENOMEM;
+ memcpy(dirty_block_ptr[gd_index]->buf, metadata_buffer, fs->blksz);
+ dirty_block_ptr[gd_index++]->blknr = blknr;
+
+ return 0;
+}
+
+void print_revoke_blks(char *revk_blk)
+{
+ int offset;
+ int max;
+ long int blocknr;
+ struct journal_revoke_header_t *header;
+
+ if (revk_blk == NULL)
+ return;
+
+ header = (struct journal_revoke_header_t *) revk_blk;
+ offset = sizeof(struct journal_revoke_header_t);
+ max = be32_to_cpu(header->r_count);
+ printf("total bytes %d\n", max);
+
+ while (offset < max) {
+ blocknr = be32_to_cpu(*((__be32 *)(revk_blk + offset)));
+ printf("revoke blknr is %ld\n", blocknr);
+ offset += 4;
+ }
+}
+
+static struct revoke_blk_list *_get_node(void)
+{
+ struct revoke_blk_list *tmp_node;
+ tmp_node = zalloc(sizeof(struct revoke_blk_list));
+ if (tmp_node == NULL)
+ return NULL;
+ tmp_node->content = NULL;
+ tmp_node->next = NULL;
+
+ return tmp_node;
+}
+
+void ext4fs_push_revoke_blk(char *buffer)
+{
+ struct revoke_blk_list *node = NULL;
+ struct ext_filesystem *fs = get_fs();
+ if (buffer == NULL) {
+ printf("buffer ptr is NULL\n");
+ return;
+ }
+ node = _get_node();
+ if (!node) {
+ printf("_get_node: malloc failed\n");
+ return;
+ }
+
+ node->content = zalloc(fs->blksz);
+ if (node->content == NULL)
+ return;
+ memcpy(node->content, buffer, fs->blksz);
+
+ if (first_node == true) {
+ revk_blk_list = node;
+ prev_node = node;
+ first_node = false;
+ } else {
+ prev_node->next = node;
+ prev_node = node;
+ }
+}
+
+void ext4fs_free_revoke_blks(void)
+{
+ struct revoke_blk_list *tmp_node = revk_blk_list;
+ struct revoke_blk_list *next_node = NULL;
+
+ while (tmp_node != NULL) {
+ free(tmp_node->content);
+ tmp_node = tmp_node->next;
+ }
+
+ tmp_node = revk_blk_list;
+ while (tmp_node != NULL) {
+ next_node = tmp_node->next;
+ free(tmp_node);
+ tmp_node = next_node;
+ }
+
+ revk_blk_list = NULL;
+ prev_node = NULL;
+ first_node = true;
+}
+
+int check_blknr_for_revoke(long int blknr, int sequence_no)
+{
+ struct journal_revoke_header_t *header;
+ int offset;
+ int max;
+ long int blocknr;
+ char *revk_blk;
+ struct revoke_blk_list *tmp_revk_node = revk_blk_list;
+ while (tmp_revk_node != NULL) {
+ revk_blk = tmp_revk_node->content;
+
+ header = (struct journal_revoke_header_t *) revk_blk;
+ if (sequence_no < be32_to_cpu(header->r_header.h_sequence)) {
+ offset = sizeof(struct journal_revoke_header_t);
+ max = be32_to_cpu(header->r_count);
+
+ while (offset < max) {
+ blocknr = be32_to_cpu(*((__be32 *)
+ (revk_blk + offset)));
+ if (blocknr == blknr)
+ goto found;
+ offset += 4;
+ }
+ }
+ tmp_revk_node = tmp_revk_node->next;
+ }
+
+ return -1;
+
+found:
+ return 0;
+}
+
+/*
+ * This function parses the journal blocks and replays the
+ * suceessful transactions. A transaction is successfull
+ * if commit block is found for a descriptor block
+ * The tags in descriptor block contain the disk block
+ * numbers of the metadata to be replayed
+ */
+void recover_transaction(int prev_desc_logical_no)
+{
+ struct ext2_inode inode_journal;
+ struct ext_filesystem *fs = get_fs();
+ struct journal_header_t *jdb;
+ long int blknr;
+ char *p_jdb;
+ int ofs, flags;
+ int i;
+ struct ext3_journal_block_tag *tag;
+ char *temp_buff = zalloc(fs->blksz);
+ char *metadata_buff = zalloc(fs->blksz);
+ if (!temp_buff || !metadata_buff)
+ goto fail;
+ i = prev_desc_logical_no;
+ ext4fs_read_inode(ext4fs_root, EXT2_JOURNAL_INO,
+ (struct ext2_inode *)&inode_journal);
+ blknr = read_allocated_block((struct ext2_inode *)
+ &inode_journal, i, NULL);
+ ext4fs_devread((lbaint_t)blknr * fs->sect_perblk, 0, fs->blksz,
+ temp_buff);
+ p_jdb = (char *)temp_buff;
+ jdb = (struct journal_header_t *) temp_buff;
+ ofs = sizeof(struct journal_header_t);
+
+ do {
+ tag = (struct ext3_journal_block_tag *)(p_jdb + ofs);
+ ofs += sizeof(struct ext3_journal_block_tag);
+
+ if (ofs > fs->blksz)
+ break;
+
+ flags = be32_to_cpu(tag->flags);
+ if (!(flags & EXT3_JOURNAL_FLAG_SAME_UUID))
+ ofs += 16;
+
+ i++;
+ debug("\t\ttag %u\n", be32_to_cpu(tag->block));
+ if (revk_blk_list != NULL) {
+ if (check_blknr_for_revoke(be32_to_cpu(tag->block),
+ be32_to_cpu(jdb->h_sequence)) == 0)
+ continue;
+ }
+ blknr = read_allocated_block(&inode_journal, i, NULL);
+ ext4fs_devread((lbaint_t)blknr * fs->sect_perblk, 0,
+ fs->blksz, metadata_buff);
+ put_ext4((uint64_t)((uint64_t)be32_to_cpu(tag->block) * (uint64_t)fs->blksz),
+ metadata_buff, (uint32_t) fs->blksz);
+ } while (!(flags & EXT3_JOURNAL_FLAG_LAST_TAG));
+fail:
+ free(temp_buff);
+ free(metadata_buff);
+}
+
+void print_jrnl_status(int recovery_flag)
+{
+ if (recovery_flag == RECOVER)
+ printf("Journal Recovery Completed\n");
+ else
+ printf("Journal Scan Completed\n");
+}
+
+int ext4fs_check_journal_state(int recovery_flag)
+{
+ int i;
+ int DB_FOUND = NO;
+ long int blknr;
+ int transaction_state = TRANSACTION_COMPLETE;
+ int prev_desc_logical_no = 0;
+ int curr_desc_logical_no = 0;
+ int ofs, flags;
+ struct ext2_inode inode_journal;
+ struct journal_superblock_t *jsb = NULL;
+ struct journal_header_t *jdb = NULL;
+ char *p_jdb = NULL;
+ struct ext3_journal_block_tag *tag = NULL;
+ char *temp_buff = NULL;
+ char *temp_buff1 = NULL;
+ struct ext_filesystem *fs = get_fs();
+
+ if (le32_to_cpu(fs->sb->feature_ro_compat) & EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)
+ return 0;
+
+ temp_buff = zalloc(fs->blksz);
+ if (!temp_buff)
+ return -ENOMEM;
+ temp_buff1 = zalloc(fs->blksz);
+ if (!temp_buff1) {
+ free(temp_buff);
+ return -ENOMEM;
+ }
+
+ ext4fs_read_inode(ext4fs_root, EXT2_JOURNAL_INO, &inode_journal);
+ blknr = read_allocated_block(&inode_journal, EXT2_JOURNAL_SUPERBLOCK,
+ NULL);
+ ext4fs_devread((lbaint_t)blknr * fs->sect_perblk, 0, fs->blksz,
+ temp_buff);
+ jsb = (struct journal_superblock_t *) temp_buff;
+
+ if (le32_to_cpu(fs->sb->feature_incompat) & EXT3_FEATURE_INCOMPAT_RECOVER) {
+ if (recovery_flag == RECOVER)
+ printf("Recovery required\n");
+ } else {
+ if (recovery_flag == RECOVER)
+ printf("File System is consistent\n");
+ goto end;
+ }
+
+ if (be32_to_cpu(jsb->s_start) == 0)
+ goto end;
+
+ if (!(jsb->s_feature_compat &
+ cpu_to_be32(JBD2_FEATURE_COMPAT_CHECKSUM)))
+ jsb->s_feature_compat |=
+ cpu_to_be32(JBD2_FEATURE_COMPAT_CHECKSUM);
+
+ i = be32_to_cpu(jsb->s_first);
+ while (1) {
+ blknr = read_allocated_block(&inode_journal, i, NULL);
+ memset(temp_buff1, '\0', fs->blksz);
+ ext4fs_devread((lbaint_t)blknr * fs->sect_perblk,
+ 0, fs->blksz, temp_buff1);
+ jdb = (struct journal_header_t *) temp_buff1;
+
+ if (be32_to_cpu(jdb->h_blocktype) ==
+ EXT3_JOURNAL_DESCRIPTOR_BLOCK) {
+ if (be32_to_cpu(jdb->h_sequence) !=
+ be32_to_cpu(jsb->s_sequence)) {
+ print_jrnl_status(recovery_flag);
+ break;
+ }
+
+ curr_desc_logical_no = i;
+ if (transaction_state == TRANSACTION_COMPLETE)
+ transaction_state = TRANSACTION_RUNNING;
+ else
+ return -1;
+ p_jdb = (char *)temp_buff1;
+ ofs = sizeof(struct journal_header_t);
+ do {
+ tag = (struct ext3_journal_block_tag *)
+ (p_jdb + ofs);
+ ofs += sizeof(struct ext3_journal_block_tag);
+ if (ofs > fs->blksz)
+ break;
+ flags = be32_to_cpu(tag->flags);
+ if (!(flags & EXT3_JOURNAL_FLAG_SAME_UUID))
+ ofs += 16;
+ i++;
+ debug("\t\ttag %u\n", be32_to_cpu(tag->block));
+ } while (!(flags & EXT3_JOURNAL_FLAG_LAST_TAG));
+ i++;
+ DB_FOUND = YES;
+ } else if (be32_to_cpu(jdb->h_blocktype) ==
+ EXT3_JOURNAL_COMMIT_BLOCK) {
+ if (be32_to_cpu(jdb->h_sequence) !=
+ be32_to_cpu(jsb->s_sequence)) {
+ print_jrnl_status(recovery_flag);
+ break;
+ }
+
+ if (transaction_state == TRANSACTION_RUNNING ||
+ (DB_FOUND == NO)) {
+ transaction_state = TRANSACTION_COMPLETE;
+ i++;
+ jsb->s_sequence =
+ cpu_to_be32(be32_to_cpu(
+ jsb->s_sequence) + 1);
+ }
+ prev_desc_logical_no = curr_desc_logical_no;
+ if ((recovery_flag == RECOVER) && (DB_FOUND == YES))
+ recover_transaction(prev_desc_logical_no);
+
+ DB_FOUND = NO;
+ } else if (be32_to_cpu(jdb->h_blocktype) ==
+ EXT3_JOURNAL_REVOKE_BLOCK) {
+ if (be32_to_cpu(jdb->h_sequence) !=
+ be32_to_cpu(jsb->s_sequence)) {
+ print_jrnl_status(recovery_flag);
+ break;
+ }
+ if (recovery_flag == SCAN)
+ ext4fs_push_revoke_blk((char *)jdb);
+ i++;
+ } else {
+ debug("Else Case\n");
+ if (be32_to_cpu(jdb->h_sequence) !=
+ be32_to_cpu(jsb->s_sequence)) {
+ print_jrnl_status(recovery_flag);
+ break;
+ }
+ }
+ }
+
+end:
+ if (recovery_flag == RECOVER) {
+ uint32_t new_feature_incompat;
+ jsb->s_start = cpu_to_be32(1);
+ jsb->s_sequence = cpu_to_be32(be32_to_cpu(jsb->s_sequence) + 1);
+ /* get the superblock */
+ ext4_read_superblock((char *)fs->sb);
+ new_feature_incompat = le32_to_cpu(fs->sb->feature_incompat);
+ new_feature_incompat |= EXT3_FEATURE_INCOMPAT_RECOVER;
+ fs->sb->feature_incompat = cpu_to_le32(new_feature_incompat);
+
+ /* Update the super block */
+ put_ext4((uint64_t) (SUPERBLOCK_SIZE),
+ (struct ext2_sblock *)fs->sb,
+ (uint32_t) SUPERBLOCK_SIZE);
+ ext4_read_superblock((char *)fs->sb);
+
+ blknr = read_allocated_block(&inode_journal,
+ EXT2_JOURNAL_SUPERBLOCK, NULL);
+ put_ext4((uint64_t) ((uint64_t)blknr * (uint64_t)fs->blksz),
+ (struct journal_superblock_t *)temp_buff,
+ (uint32_t) fs->blksz);
+ ext4fs_free_revoke_blks();
+ }
+ free(temp_buff);
+ free(temp_buff1);
+
+ return 0;
+}
+
+static void update_descriptor_block(long int blknr)
+{
+ int i;
+ long int jsb_blknr;
+ struct journal_header_t jdb;
+ struct ext3_journal_block_tag tag;
+ struct ext2_inode inode_journal;
+ struct journal_superblock_t *jsb = NULL;
+ char *buf = NULL;
+ char *temp = NULL;
+ struct ext_filesystem *fs = get_fs();
+ char *temp_buff = zalloc(fs->blksz);
+ if (!temp_buff)
+ return;
+
+ ext4fs_read_inode(ext4fs_root, EXT2_JOURNAL_INO, &inode_journal);
+ jsb_blknr = read_allocated_block(&inode_journal,
+ EXT2_JOURNAL_SUPERBLOCK, NULL);
+ ext4fs_devread((lbaint_t)jsb_blknr * fs->sect_perblk, 0, fs->blksz,
+ temp_buff);
+ jsb = (struct journal_superblock_t *) temp_buff;
+
+ jdb.h_blocktype = cpu_to_be32(EXT3_JOURNAL_DESCRIPTOR_BLOCK);
+ jdb.h_magic = cpu_to_be32(EXT3_JOURNAL_MAGIC_NUMBER);
+ jdb.h_sequence = jsb->s_sequence;
+ buf = zalloc(fs->blksz);
+ if (!buf) {
+ free(temp_buff);
+ return;
+ }
+ temp = buf;
+ memcpy(buf, &jdb, sizeof(struct journal_header_t));
+ temp += sizeof(struct journal_header_t);
+
+ for (i = 0; i < MAX_JOURNAL_ENTRIES; i++) {
+ if (journal_ptr[i]->blknr == -1)
+ break;
+
+ tag.block = cpu_to_be32(journal_ptr[i]->blknr);
+ tag.flags = cpu_to_be32(EXT3_JOURNAL_FLAG_SAME_UUID);
+ memcpy(temp, &tag, sizeof(struct ext3_journal_block_tag));
+ temp = temp + sizeof(struct ext3_journal_block_tag);
+ }
+
+ tag.block = cpu_to_be32(journal_ptr[--i]->blknr);
+ tag.flags = cpu_to_be32(EXT3_JOURNAL_FLAG_LAST_TAG);
+ memcpy(temp - sizeof(struct ext3_journal_block_tag), &tag,
+ sizeof(struct ext3_journal_block_tag));
+ put_ext4((uint64_t) ((uint64_t)blknr * (uint64_t)fs->blksz), buf, (uint32_t) fs->blksz);
+
+ free(temp_buff);
+ free(buf);
+}
+
+static void update_commit_block(long int blknr)
+{
+ struct journal_header_t jdb;
+ struct ext_filesystem *fs = get_fs();
+ char *buf = NULL;
+ struct ext2_inode inode_journal;
+ struct journal_superblock_t *jsb;
+ long int jsb_blknr;
+ char *temp_buff = zalloc(fs->blksz);
+ if (!temp_buff)
+ return;
+
+ ext4fs_read_inode(ext4fs_root, EXT2_JOURNAL_INO,
+ &inode_journal);
+ jsb_blknr = read_allocated_block(&inode_journal,
+ EXT2_JOURNAL_SUPERBLOCK, NULL);
+ ext4fs_devread((lbaint_t)jsb_blknr * fs->sect_perblk, 0, fs->blksz,
+ temp_buff);
+ jsb = (struct journal_superblock_t *) temp_buff;
+
+ jdb.h_blocktype = cpu_to_be32(EXT3_JOURNAL_COMMIT_BLOCK);
+ jdb.h_magic = cpu_to_be32(EXT3_JOURNAL_MAGIC_NUMBER);
+ jdb.h_sequence = jsb->s_sequence;
+ buf = zalloc(fs->blksz);
+ if (!buf) {
+ free(temp_buff);
+ return;
+ }
+ memcpy(buf, &jdb, sizeof(struct journal_header_t));
+ put_ext4((uint64_t) ((uint64_t)blknr * (uint64_t)fs->blksz), buf, (uint32_t) fs->blksz);
+
+ free(temp_buff);
+ free(buf);
+}
+
+void ext4fs_update_journal(void)
+{
+ struct ext2_inode inode_journal;
+ struct ext_filesystem *fs = get_fs();
+ long int blknr;
+ int i;
+
+ if (!(fs->sb->feature_compatibility & EXT4_FEATURE_COMPAT_HAS_JOURNAL))
+ return;
+
+ ext4fs_read_inode(ext4fs_root, EXT2_JOURNAL_INO, &inode_journal);
+ blknr = read_allocated_block(&inode_journal, jrnl_blk_idx++, NULL);
+ update_descriptor_block(blknr);
+ for (i = 0; i < MAX_JOURNAL_ENTRIES; i++) {
+ if (journal_ptr[i]->blknr == -1)
+ break;
+ blknr = read_allocated_block(&inode_journal, jrnl_blk_idx++,
+ NULL);
+ put_ext4((uint64_t) ((uint64_t)blknr * (uint64_t)fs->blksz),
+ journal_ptr[i]->buf, fs->blksz);
+ }
+ blknr = read_allocated_block(&inode_journal, jrnl_blk_idx++, NULL);
+ update_commit_block(blknr);
+ printf("update journal finished\n");
+}
diff --git a/roms/u-boot/fs/ext4/ext4_journal.h b/roms/u-boot/fs/ext4/ext4_journal.h
new file mode 100644
index 000000000..43fb8e766
--- /dev/null
+++ b/roms/u-boot/fs/ext4/ext4_journal.h
@@ -0,0 +1,127 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * (C) Copyright 2011 - 2012 Samsung Electronics
+ * EXT4 filesystem implementation in Uboot by
+ * Uma Shankar <uma.shankar@samsung.com>
+ * Manjunatha C Achar <a.manjunatha@samsung.com>
+ *
+ * Journal data structures and headers for Journaling feature of ext4
+ * have been referred from JBD2 (Journaling Block device 2)
+ * implementation in Linux Kernel.
+ *
+ * Written by Stephen C. Tweedie <sct@redhat.com>
+ *
+ * Copyright 1998-2000 Red Hat, Inc --- All Rights Reserved
+ */
+
+#ifndef __EXT4_JRNL__
+#define __EXT4_JRNL__
+
+#define EXT4_FEATURE_COMPAT_HAS_JOURNAL 0x0004
+
+#define EXT2_JOURNAL_INO 8 /* Journal inode */
+#define EXT2_JOURNAL_SUPERBLOCK 0 /* Journal Superblock number */
+
+#define JBD2_FEATURE_COMPAT_CHECKSUM 0x00000001
+#define EXT3_JOURNAL_MAGIC_NUMBER 0xc03b3998U
+#define TRANSACTION_RUNNING 1
+#define TRANSACTION_COMPLETE 0
+#define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004 /* Needs recovery */
+#define EXT3_JOURNAL_DESCRIPTOR_BLOCK 1
+#define EXT3_JOURNAL_COMMIT_BLOCK 2
+#define EXT3_JOURNAL_SUPERBLOCK_V1 3
+#define EXT3_JOURNAL_SUPERBLOCK_V2 4
+#define EXT3_JOURNAL_REVOKE_BLOCK 5
+#define EXT3_JOURNAL_FLAG_ESCAPE 1
+#define EXT3_JOURNAL_FLAG_SAME_UUID 2
+#define EXT3_JOURNAL_FLAG_DELETED 4
+#define EXT3_JOURNAL_FLAG_LAST_TAG 8
+
+/* Maximum entries in 1 journal transaction */
+#define MAX_JOURNAL_ENTRIES 100
+struct journal_log {
+ char *buf;
+ int blknr;
+};
+
+struct dirty_blocks {
+ char *buf;
+ int blknr;
+};
+
+/* Standard header for all descriptor blocks: */
+struct journal_header_t {
+ __be32 h_magic;
+ __be32 h_blocktype;
+ __be32 h_sequence;
+};
+
+/* The journal superblock. All fields are in big-endian byte order. */
+struct journal_superblock_t {
+ /* 0x0000 */
+ struct journal_header_t s_header;
+
+ /* Static information describing the journal */
+ __be32 s_blocksize; /* journal device blocksize */
+ __be32 s_maxlen; /* total blocks in journal file */
+ __be32 s_first; /* first block of log information */
+
+ /* Dynamic information describing the current state of the log */
+ __be32 s_sequence; /* first commit ID expected in log */
+ __be32 s_start; /* blocknr of start of log */
+
+ /* Error value, as set by journal_abort(). */
+ __be32 s_errno;
+
+ /* Remaining fields are only valid in a version-2 superblock */
+ __be32 s_feature_compat; /* compatible feature set */
+ __be32 s_feature_incompat; /* incompatible feature set */
+ __be32 s_feature_ro_compat; /* readonly-compatible feature set */
+ /* 0x0030 */
+ __u8 s_uuid[16]; /* 128-bit uuid for journal */
+
+ /* 0x0040 */
+ __be32 s_nr_users; /* Nr of filesystems sharing log */
+
+ __be32 s_dynsuper; /* Blocknr of dynamic superblock copy */
+
+ /* 0x0048 */
+ __be32 s_max_transaction; /* Limit of journal blocks per trans. */
+ __be32 s_max_trans_data; /* Limit of data blocks per trans. */
+
+ /* 0x0050 */
+ __be32 s_padding[44];
+
+ /* 0x0100 */
+ __u8 s_users[16 * 48]; /* ids of all fs'es sharing the log */
+ /* 0x0400 */
+} ;
+
+struct ext3_journal_block_tag {
+ __be32 block;
+ __be32 flags;
+};
+
+struct journal_revoke_header_t {
+ struct journal_header_t r_header;
+ __be32 r_count; /* Count of bytes used in the block */
+};
+
+struct revoke_blk_list {
+ char *content; /* revoke block itself */
+ struct revoke_blk_list *next;
+};
+
+extern struct ext2_data *ext4fs_root;
+
+int ext4fs_init_journal(void);
+int ext4fs_log_gdt(char *gd_table);
+int ext4fs_check_journal_state(int recovery_flag);
+int ext4fs_log_journal(char *journal_buffer, uint32_t blknr);
+int ext4fs_put_metadata(char *metadata_buffer, uint32_t blknr);
+void ext4fs_update_journal(void);
+void ext4fs_dump_metadata(void);
+void ext4fs_push_revoke_blk(char *buffer);
+void ext4fs_free_journal(void);
+void ext4fs_free_revoke_blks(void);
+#endif
diff --git a/roms/u-boot/fs/ext4/ext4_write.c b/roms/u-boot/fs/ext4/ext4_write.c
new file mode 100644
index 000000000..f22af45d1
--- /dev/null
+++ b/roms/u-boot/fs/ext4/ext4_write.c
@@ -0,0 +1,1069 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2011 - 2012 Samsung Electronics
+ * EXT4 filesystem implementation in Uboot by
+ * Uma Shankar <uma.shankar@samsung.com>
+ * Manjunatha C Achar <a.manjunatha@samsung.com>
+ *
+ * ext4ls and ext4load : Based on ext2 ls and load support in Uboot.
+ * Ext4 read optimization taken from Open-Moko
+ * Qi bootloader
+ *
+ * (C) Copyright 2004
+ * esd gmbh <www.esd-electronics.com>
+ * Reinhard Arlt <reinhard.arlt@esd-electronics.com>
+ *
+ * based on code from grub2 fs/ext2.c and fs/fshelp.c by
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+ *
+ * ext4write : Based on generic ext4 protocol.
+ */
+
+
+#include <common.h>
+#include <blk.h>
+#include <log.h>
+#include <malloc.h>
+#include <memalign.h>
+#include <part.h>
+#include <linux/stat.h>
+#include <div64.h>
+#include "ext4_common.h"
+
+static inline void ext4fs_sb_free_inodes_inc(struct ext2_sblock *sb)
+{
+ sb->free_inodes = cpu_to_le32(le32_to_cpu(sb->free_inodes) + 1);
+}
+
+static inline void ext4fs_sb_free_blocks_inc(struct ext2_sblock *sb)
+{
+ sb->free_blocks = cpu_to_le32(le32_to_cpu(sb->free_blocks) + 1);
+}
+
+static inline void ext4fs_bg_free_inodes_inc
+ (struct ext2_block_group *bg, const struct ext_filesystem *fs)
+{
+ uint32_t free_inodes = le16_to_cpu(bg->free_inodes);
+ if (fs->gdsize == 64)
+ free_inodes += le16_to_cpu(bg->free_inodes_high) << 16;
+ free_inodes++;
+
+ bg->free_inodes = cpu_to_le16(free_inodes & 0xffff);
+ if (fs->gdsize == 64)
+ bg->free_inodes_high = cpu_to_le16(free_inodes >> 16);
+}
+
+static inline void ext4fs_bg_free_blocks_inc
+ (struct ext2_block_group *bg, const struct ext_filesystem *fs)
+{
+ uint32_t free_blocks = le16_to_cpu(bg->free_blocks);
+ if (fs->gdsize == 64)
+ free_blocks += le16_to_cpu(bg->free_blocks_high) << 16;
+ free_blocks++;
+
+ bg->free_blocks = cpu_to_le16(free_blocks & 0xffff);
+ if (fs->gdsize == 64)
+ bg->free_blocks_high = cpu_to_le16(free_blocks >> 16);
+}
+
+static void ext4fs_update(void)
+{
+ short i;
+ ext4fs_update_journal();
+ struct ext_filesystem *fs = get_fs();
+ struct ext2_block_group *bgd = NULL;
+
+ /* update super block */
+ put_ext4((uint64_t)(SUPERBLOCK_SIZE),
+ (struct ext2_sblock *)fs->sb, (uint32_t)SUPERBLOCK_SIZE);
+
+ /* update block bitmaps */
+ for (i = 0; i < fs->no_blkgrp; i++) {
+ bgd = ext4fs_get_group_descriptor(fs, i);
+ bgd->bg_checksum = cpu_to_le16(ext4fs_checksum_update(i));
+ uint64_t b_bitmap_blk = ext4fs_bg_get_block_id(bgd, fs);
+ put_ext4(b_bitmap_blk * fs->blksz,
+ fs->blk_bmaps[i], fs->blksz);
+ }
+
+ /* update inode bitmaps */
+ for (i = 0; i < fs->no_blkgrp; i++) {
+ bgd = ext4fs_get_group_descriptor(fs, i);
+ uint64_t i_bitmap_blk = ext4fs_bg_get_inode_id(bgd, fs);
+ put_ext4(i_bitmap_blk * fs->blksz,
+ fs->inode_bmaps[i], fs->blksz);
+ }
+
+ /* update the block group descriptor table */
+ put_ext4((uint64_t)((uint64_t)fs->gdtable_blkno * (uint64_t)fs->blksz),
+ (struct ext2_block_group *)fs->gdtable,
+ (fs->blksz * fs->no_blk_pergdt));
+
+ ext4fs_dump_metadata();
+
+ gindex = 0;
+ gd_index = 0;
+}
+
+int ext4fs_get_bgdtable(void)
+{
+ int status;
+ struct ext_filesystem *fs = get_fs();
+ int gdsize_total = ROUND(fs->no_blkgrp * fs->gdsize, fs->blksz);
+ fs->no_blk_pergdt = gdsize_total / fs->blksz;
+
+ /* allocate memory for gdtable */
+ fs->gdtable = zalloc(gdsize_total);
+ if (!fs->gdtable)
+ return -ENOMEM;
+ /* read the group descriptor table */
+ status = ext4fs_devread((lbaint_t)fs->gdtable_blkno * fs->sect_perblk,
+ 0, fs->blksz * fs->no_blk_pergdt, fs->gdtable);
+ if (status == 0)
+ goto fail;
+
+ if (ext4fs_log_gdt(fs->gdtable)) {
+ printf("Error in ext4fs_log_gdt\n");
+ return -1;
+ }
+
+ return 0;
+fail:
+ free(fs->gdtable);
+ fs->gdtable = NULL;
+
+ return -1;
+}
+
+static void delete_single_indirect_block(struct ext2_inode *inode)
+{
+ struct ext2_block_group *bgd = NULL;
+ static int prev_bg_bmap_idx = -1;
+ uint32_t blknr;
+ int remainder;
+ int bg_idx;
+ int status;
+ uint32_t blk_per_grp = le32_to_cpu(ext4fs_root->sblock.blocks_per_group);
+ struct ext_filesystem *fs = get_fs();
+ char *journal_buffer = zalloc(fs->blksz);
+ if (!journal_buffer) {
+ printf("No memory\n");
+ return;
+ }
+
+ /* deleting the single indirect block associated with inode */
+ if (inode->b.blocks.indir_block != 0) {
+ blknr = le32_to_cpu(inode->b.blocks.indir_block);
+ debug("SIPB releasing %u\n", blknr);
+ bg_idx = blknr / blk_per_grp;
+ if (fs->blksz == 1024) {
+ remainder = blknr % blk_per_grp;
+ if (!remainder)
+ bg_idx--;
+ }
+ ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx], bg_idx);
+ /* get block group descriptor table */
+ bgd = ext4fs_get_group_descriptor(fs, bg_idx);
+ ext4fs_bg_free_blocks_inc(bgd, fs);
+ ext4fs_sb_free_blocks_inc(fs->sb);
+ /* journal backup */
+ if (prev_bg_bmap_idx != bg_idx) {
+ uint64_t b_bitmap_blk = ext4fs_bg_get_block_id(bgd, fs);
+ status = ext4fs_devread(
+ b_bitmap_blk * fs->sect_perblk,
+ 0, fs->blksz, journal_buffer);
+ if (status == 0)
+ goto fail;
+ if (ext4fs_log_journal(journal_buffer, b_bitmap_blk))
+ goto fail;
+ prev_bg_bmap_idx = bg_idx;
+ }
+ }
+fail:
+ free(journal_buffer);
+}
+
+static void delete_double_indirect_block(struct ext2_inode *inode)
+{
+ int i;
+ short status;
+ static int prev_bg_bmap_idx = -1;
+ uint32_t blknr;
+ int remainder;
+ int bg_idx;
+ uint32_t blk_per_grp = le32_to_cpu(ext4fs_root->sblock.blocks_per_group);
+ __le32 *di_buffer = NULL;
+ void *dib_start_addr = NULL;
+ struct ext2_block_group *bgd = NULL;
+ struct ext_filesystem *fs = get_fs();
+ char *journal_buffer = zalloc(fs->blksz);
+ if (!journal_buffer) {
+ printf("No memory\n");
+ return;
+ }
+
+ if (inode->b.blocks.double_indir_block != 0) {
+ di_buffer = zalloc(fs->blksz);
+ if (!di_buffer) {
+ printf("No memory\n");
+ return;
+ }
+ dib_start_addr = di_buffer;
+ blknr = le32_to_cpu(inode->b.blocks.double_indir_block);
+ status = ext4fs_devread((lbaint_t)blknr * fs->sect_perblk, 0,
+ fs->blksz, (char *)di_buffer);
+ for (i = 0; i < fs->blksz / sizeof(int); i++) {
+ if (*di_buffer == 0)
+ break;
+
+ debug("DICB releasing %u\n", *di_buffer);
+ bg_idx = le32_to_cpu(*di_buffer) / blk_per_grp;
+ if (fs->blksz == 1024) {
+ remainder = le32_to_cpu(*di_buffer) % blk_per_grp;
+ if (!remainder)
+ bg_idx--;
+ }
+ /* get block group descriptor table */
+ bgd = ext4fs_get_group_descriptor(fs, bg_idx);
+ ext4fs_reset_block_bmap(le32_to_cpu(*di_buffer),
+ fs->blk_bmaps[bg_idx], bg_idx);
+ di_buffer++;
+ ext4fs_bg_free_blocks_inc(bgd, fs);
+ ext4fs_sb_free_blocks_inc(fs->sb);
+ /* journal backup */
+ if (prev_bg_bmap_idx != bg_idx) {
+ uint64_t b_bitmap_blk =
+ ext4fs_bg_get_block_id(bgd, fs);
+ status = ext4fs_devread(b_bitmap_blk
+ * fs->sect_perblk, 0,
+ fs->blksz,
+ journal_buffer);
+ if (status == 0)
+ goto fail;
+
+ if (ext4fs_log_journal(journal_buffer,
+ b_bitmap_blk))
+ goto fail;
+ prev_bg_bmap_idx = bg_idx;
+ }
+ }
+
+ /* removing the parent double indirect block */
+ blknr = le32_to_cpu(inode->b.blocks.double_indir_block);
+ bg_idx = blknr / blk_per_grp;
+ if (fs->blksz == 1024) {
+ remainder = blknr % blk_per_grp;
+ if (!remainder)
+ bg_idx--;
+ }
+ /* get block group descriptor table */
+ bgd = ext4fs_get_group_descriptor(fs, bg_idx);
+ ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx], bg_idx);
+ ext4fs_bg_free_blocks_inc(bgd, fs);
+ ext4fs_sb_free_blocks_inc(fs->sb);
+ /* journal backup */
+ if (prev_bg_bmap_idx != bg_idx) {
+ uint64_t b_bitmap_blk = ext4fs_bg_get_block_id(bgd, fs);
+ status = ext4fs_devread(b_bitmap_blk * fs->sect_perblk,
+ 0, fs->blksz, journal_buffer);
+ if (status == 0)
+ goto fail;
+
+ if (ext4fs_log_journal(journal_buffer, b_bitmap_blk))
+ goto fail;
+ prev_bg_bmap_idx = bg_idx;
+ }
+ debug("DIPB releasing %d\n", blknr);
+ }
+fail:
+ free(dib_start_addr);
+ free(journal_buffer);
+}
+
+static void delete_triple_indirect_block(struct ext2_inode *inode)
+{
+ int i, j;
+ short status;
+ static int prev_bg_bmap_idx = -1;
+ uint32_t blknr;
+ int remainder;
+ int bg_idx;
+ uint32_t blk_per_grp = le32_to_cpu(ext4fs_root->sblock.blocks_per_group);
+ __le32 *tigp_buffer = NULL;
+ void *tib_start_addr = NULL;
+ __le32 *tip_buffer = NULL;
+ void *tipb_start_addr = NULL;
+ struct ext2_block_group *bgd = NULL;
+ struct ext_filesystem *fs = get_fs();
+ char *journal_buffer = zalloc(fs->blksz);
+ if (!journal_buffer) {
+ printf("No memory\n");
+ return;
+ }
+
+ if (inode->b.blocks.triple_indir_block != 0) {
+ tigp_buffer = zalloc(fs->blksz);
+ if (!tigp_buffer) {
+ printf("No memory\n");
+ return;
+ }
+ tib_start_addr = tigp_buffer;
+ blknr = le32_to_cpu(inode->b.blocks.triple_indir_block);
+ status = ext4fs_devread((lbaint_t)blknr * fs->sect_perblk, 0,
+ fs->blksz, (char *)tigp_buffer);
+ for (i = 0; i < fs->blksz / sizeof(int); i++) {
+ if (*tigp_buffer == 0)
+ break;
+ debug("tigp buffer releasing %u\n", *tigp_buffer);
+
+ tip_buffer = zalloc(fs->blksz);
+ if (!tip_buffer)
+ goto fail;
+ tipb_start_addr = tip_buffer;
+ status = ext4fs_devread((lbaint_t)le32_to_cpu(*tigp_buffer) *
+ fs->sect_perblk, 0, fs->blksz,
+ (char *)tip_buffer);
+ for (j = 0; j < fs->blksz / sizeof(int); j++) {
+ if (le32_to_cpu(*tip_buffer) == 0)
+ break;
+ bg_idx = le32_to_cpu(*tip_buffer) / blk_per_grp;
+ if (fs->blksz == 1024) {
+ remainder = le32_to_cpu(*tip_buffer) % blk_per_grp;
+ if (!remainder)
+ bg_idx--;
+ }
+
+ ext4fs_reset_block_bmap(le32_to_cpu(*tip_buffer),
+ fs->blk_bmaps[bg_idx],
+ bg_idx);
+
+ tip_buffer++;
+ /* get block group descriptor table */
+ bgd = ext4fs_get_group_descriptor(fs, bg_idx);
+ ext4fs_bg_free_blocks_inc(bgd, fs);
+ ext4fs_sb_free_blocks_inc(fs->sb);
+ /* journal backup */
+ if (prev_bg_bmap_idx != bg_idx) {
+ uint64_t b_bitmap_blk =
+ ext4fs_bg_get_block_id(bgd, fs);
+ status =
+ ext4fs_devread(
+ b_bitmap_blk *
+ fs->sect_perblk, 0,
+ fs->blksz,
+ journal_buffer);
+ if (status == 0)
+ goto fail;
+
+ if (ext4fs_log_journal(journal_buffer,
+ b_bitmap_blk))
+ goto fail;
+ prev_bg_bmap_idx = bg_idx;
+ }
+ }
+ free(tipb_start_addr);
+ tipb_start_addr = NULL;
+
+ /*
+ * removing the grand parent blocks
+ * which is connected to inode
+ */
+ bg_idx = le32_to_cpu(*tigp_buffer) / blk_per_grp;
+ if (fs->blksz == 1024) {
+ remainder = le32_to_cpu(*tigp_buffer) % blk_per_grp;
+ if (!remainder)
+ bg_idx--;
+ }
+ ext4fs_reset_block_bmap(le32_to_cpu(*tigp_buffer),
+ fs->blk_bmaps[bg_idx], bg_idx);
+
+ tigp_buffer++;
+ /* get block group descriptor table */
+ bgd = ext4fs_get_group_descriptor(fs, bg_idx);
+ ext4fs_bg_free_blocks_inc(bgd, fs);
+ ext4fs_sb_free_blocks_inc(fs->sb);
+ /* journal backup */
+ if (prev_bg_bmap_idx != bg_idx) {
+ uint64_t b_bitmap_blk =
+ ext4fs_bg_get_block_id(bgd, fs);
+ memset(journal_buffer, '\0', fs->blksz);
+ status = ext4fs_devread(b_bitmap_blk *
+ fs->sect_perblk, 0,
+ fs->blksz,
+ journal_buffer);
+ if (status == 0)
+ goto fail;
+
+ if (ext4fs_log_journal(journal_buffer,
+ b_bitmap_blk))
+ goto fail;
+ prev_bg_bmap_idx = bg_idx;
+ }
+ }
+
+ /* removing the grand parent triple indirect block */
+ blknr = le32_to_cpu(inode->b.blocks.triple_indir_block);
+ bg_idx = blknr / blk_per_grp;
+ if (fs->blksz == 1024) {
+ remainder = blknr % blk_per_grp;
+ if (!remainder)
+ bg_idx--;
+ }
+ ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx], bg_idx);
+ /* get block group descriptor table */
+ bgd = ext4fs_get_group_descriptor(fs, bg_idx);
+ ext4fs_bg_free_blocks_inc(bgd, fs);
+ ext4fs_sb_free_blocks_inc(fs->sb);
+ /* journal backup */
+ if (prev_bg_bmap_idx != bg_idx) {
+ uint64_t b_bitmap_blk = ext4fs_bg_get_block_id(bgd, fs);
+ status = ext4fs_devread(b_bitmap_blk * fs->sect_perblk,
+ 0, fs->blksz, journal_buffer);
+ if (status == 0)
+ goto fail;
+
+ if (ext4fs_log_journal(journal_buffer, b_bitmap_blk))
+ goto fail;
+ prev_bg_bmap_idx = bg_idx;
+ }
+ debug("tigp buffer itself releasing %d\n", blknr);
+ }
+fail:
+ free(tib_start_addr);
+ free(tipb_start_addr);
+ free(journal_buffer);
+}
+
+static int ext4fs_delete_file(int inodeno)
+{
+ struct ext2_inode inode;
+ short status;
+ int i;
+ int remainder;
+ long int blknr;
+ int bg_idx;
+ int ibmap_idx;
+ char *read_buffer = NULL;
+ char *start_block_address = NULL;
+ uint32_t no_blocks;
+
+ static int prev_bg_bmap_idx = -1;
+ unsigned int inodes_per_block;
+ uint32_t blkno;
+ unsigned int blkoff;
+ uint32_t blk_per_grp = le32_to_cpu(ext4fs_root->sblock.blocks_per_group);
+ uint32_t inode_per_grp = le32_to_cpu(ext4fs_root->sblock.inodes_per_group);
+ struct ext2_inode *inode_buffer = NULL;
+ struct ext2_block_group *bgd = NULL;
+ struct ext_filesystem *fs = get_fs();
+ char *journal_buffer = zalloc(fs->blksz);
+ if (!journal_buffer)
+ return -ENOMEM;
+ status = ext4fs_read_inode(ext4fs_root, inodeno, &inode);
+ if (status == 0)
+ goto fail;
+
+ /* read the block no allocated to a file */
+ no_blocks = le32_to_cpu(inode.size) / fs->blksz;
+ if (le32_to_cpu(inode.size) % fs->blksz)
+ no_blocks++;
+
+ /*
+ * special case for symlinks whose target are small enough that
+ *it fits in struct ext2_inode.b.symlink: no block had been allocated
+ */
+ if ((le16_to_cpu(inode.mode) & S_IFLNK) &&
+ le32_to_cpu(inode.size) <= sizeof(inode.b.symlink)) {
+ no_blocks = 0;
+ }
+
+ if (le32_to_cpu(inode.flags) & EXT4_EXTENTS_FL) {
+ /* FIXME delete extent index blocks, i.e. eh_depth >= 1 */
+ struct ext4_extent_header *eh =
+ (struct ext4_extent_header *)
+ inode.b.blocks.dir_blocks;
+ debug("del: dep=%d entries=%d\n", eh->eh_depth, eh->eh_entries);
+ } else {
+ delete_single_indirect_block(&inode);
+ delete_double_indirect_block(&inode);
+ delete_triple_indirect_block(&inode);
+ }
+
+ /* release data blocks */
+ for (i = 0; i < no_blocks; i++) {
+ blknr = read_allocated_block(&inode, i, NULL);
+ if (blknr == 0)
+ continue;
+ if (blknr < 0)
+ goto fail;
+ bg_idx = blknr / blk_per_grp;
+ if (fs->blksz == 1024) {
+ remainder = blknr % blk_per_grp;
+ if (!remainder)
+ bg_idx--;
+ }
+ ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx],
+ bg_idx);
+ debug("EXT4 Block releasing %ld: %d\n", blknr, bg_idx);
+
+ /* get block group descriptor table */
+ bgd = ext4fs_get_group_descriptor(fs, bg_idx);
+ ext4fs_bg_free_blocks_inc(bgd, fs);
+ ext4fs_sb_free_blocks_inc(fs->sb);
+ /* journal backup */
+ if (prev_bg_bmap_idx != bg_idx) {
+ uint64_t b_bitmap_blk = ext4fs_bg_get_block_id(bgd, fs);
+ status = ext4fs_devread(b_bitmap_blk * fs->sect_perblk,
+ 0, fs->blksz,
+ journal_buffer);
+ if (status == 0)
+ goto fail;
+ if (ext4fs_log_journal(journal_buffer, b_bitmap_blk))
+ goto fail;
+ prev_bg_bmap_idx = bg_idx;
+ }
+ }
+
+ /* release inode */
+ /* from the inode no to blockno */
+ inodes_per_block = fs->blksz / fs->inodesz;
+ ibmap_idx = inodeno / inode_per_grp;
+
+ /* get the block no */
+ inodeno--;
+ /* get block group descriptor table */
+ bgd = ext4fs_get_group_descriptor(fs, ibmap_idx);
+ blkno = ext4fs_bg_get_inode_table_id(bgd, fs) +
+ (inodeno % inode_per_grp) / inodes_per_block;
+
+ /* get the offset of the inode */
+ blkoff = ((inodeno) % inodes_per_block) * fs->inodesz;
+
+ /* read the block no containing the inode */
+ read_buffer = zalloc(fs->blksz);
+ if (!read_buffer)
+ goto fail;
+ start_block_address = read_buffer;
+ status = ext4fs_devread((lbaint_t)blkno * fs->sect_perblk,
+ 0, fs->blksz, read_buffer);
+ if (status == 0)
+ goto fail;
+
+ if (ext4fs_log_journal(read_buffer, blkno))
+ goto fail;
+
+ read_buffer = read_buffer + blkoff;
+ inode_buffer = (struct ext2_inode *)read_buffer;
+ memset(inode_buffer, '\0', fs->inodesz);
+
+ /* write the inode to original position in inode table */
+ if (ext4fs_put_metadata(start_block_address, blkno))
+ goto fail;
+
+ /* update the respective inode bitmaps */
+ inodeno++;
+ ext4fs_reset_inode_bmap(inodeno, fs->inode_bmaps[ibmap_idx], ibmap_idx);
+ ext4fs_bg_free_inodes_inc(bgd, fs);
+ ext4fs_sb_free_inodes_inc(fs->sb);
+ /* journal backup */
+ memset(journal_buffer, '\0', fs->blksz);
+ status = ext4fs_devread(ext4fs_bg_get_inode_id(bgd, fs) *
+ fs->sect_perblk, 0, fs->blksz, journal_buffer);
+ if (status == 0)
+ goto fail;
+ if (ext4fs_log_journal(journal_buffer, ext4fs_bg_get_inode_id(bgd, fs)))
+ goto fail;
+
+ ext4fs_update();
+ ext4fs_deinit();
+ ext4fs_reinit_global();
+
+ if (ext4fs_init() != 0) {
+ printf("error in File System init\n");
+ goto fail;
+ }
+
+ free(start_block_address);
+ free(journal_buffer);
+
+ return 0;
+fail:
+ free(start_block_address);
+ free(journal_buffer);
+
+ return -1;
+}
+
+int ext4fs_init(void)
+{
+ short status;
+ int i;
+ uint32_t real_free_blocks = 0;
+ struct ext_filesystem *fs = get_fs();
+
+ /* populate fs */
+ fs->blksz = EXT2_BLOCK_SIZE(ext4fs_root);
+ fs->sect_perblk = fs->blksz >> fs->dev_desc->log2blksz;
+
+ /* get the superblock */
+ fs->sb = zalloc(SUPERBLOCK_SIZE);
+ if (!fs->sb)
+ return -ENOMEM;
+ if (!ext4_read_superblock((char *)fs->sb))
+ goto fail;
+
+ /* init journal */
+ if (ext4fs_init_journal())
+ goto fail;
+
+ /* get total no of blockgroups */
+ fs->no_blkgrp = (uint32_t)ext4fs_div_roundup(
+ le32_to_cpu(ext4fs_root->sblock.total_blocks)
+ - le32_to_cpu(ext4fs_root->sblock.first_data_block),
+ le32_to_cpu(ext4fs_root->sblock.blocks_per_group));
+
+ /* get the block group descriptor table */
+ fs->gdtable_blkno = ((EXT2_MIN_BLOCK_SIZE == fs->blksz) + 1);
+ if (ext4fs_get_bgdtable() == -1) {
+ printf("Error in getting the block group descriptor table\n");
+ goto fail;
+ }
+
+ /* load all the available bitmap block of the partition */
+ fs->blk_bmaps = zalloc(fs->no_blkgrp * sizeof(char *));
+ if (!fs->blk_bmaps)
+ goto fail;
+ for (i = 0; i < fs->no_blkgrp; i++) {
+ fs->blk_bmaps[i] = zalloc(fs->blksz);
+ if (!fs->blk_bmaps[i])
+ goto fail;
+ }
+
+ for (i = 0; i < fs->no_blkgrp; i++) {
+ struct ext2_block_group *bgd =
+ ext4fs_get_group_descriptor(fs, i);
+ status = ext4fs_devread(ext4fs_bg_get_block_id(bgd, fs) *
+ fs->sect_perblk, 0,
+ fs->blksz, (char *)fs->blk_bmaps[i]);
+ if (status == 0)
+ goto fail;
+ }
+
+ /* load all the available inode bitmap of the partition */
+ fs->inode_bmaps = zalloc(fs->no_blkgrp * sizeof(unsigned char *));
+ if (!fs->inode_bmaps)
+ goto fail;
+ for (i = 0; i < fs->no_blkgrp; i++) {
+ fs->inode_bmaps[i] = zalloc(fs->blksz);
+ if (!fs->inode_bmaps[i])
+ goto fail;
+ }
+
+ for (i = 0; i < fs->no_blkgrp; i++) {
+ struct ext2_block_group *bgd =
+ ext4fs_get_group_descriptor(fs, i);
+ status = ext4fs_devread(ext4fs_bg_get_inode_id(bgd, fs) *
+ fs->sect_perblk,
+ 0, fs->blksz,
+ (char *)fs->inode_bmaps[i]);
+ if (status == 0)
+ goto fail;
+ }
+
+ /*
+ * check filesystem consistency with free blocks of file system
+ * some time we observed that superblock freeblocks does not match
+ * with the blockgroups freeblocks when improper
+ * reboot of a linux kernel
+ */
+ for (i = 0; i < fs->no_blkgrp; i++) {
+ struct ext2_block_group *bgd =
+ ext4fs_get_group_descriptor(fs, i);
+ real_free_blocks = real_free_blocks +
+ ext4fs_bg_get_free_blocks(bgd, fs);
+ }
+ if (real_free_blocks != ext4fs_sb_get_free_blocks(fs->sb))
+ ext4fs_sb_set_free_blocks(fs->sb, real_free_blocks);
+
+ return 0;
+fail:
+ ext4fs_deinit();
+
+ return -1;
+}
+
+void ext4fs_deinit(void)
+{
+ int i;
+ struct ext2_inode inode_journal;
+ struct journal_superblock_t *jsb;
+ uint32_t blknr;
+ struct ext_filesystem *fs = get_fs();
+ uint32_t new_feature_incompat;
+
+ /* free journal */
+ char *temp_buff = zalloc(fs->blksz);
+ if (temp_buff) {
+ ext4fs_read_inode(ext4fs_root, EXT2_JOURNAL_INO,
+ &inode_journal);
+ blknr = read_allocated_block(&inode_journal,
+ EXT2_JOURNAL_SUPERBLOCK, NULL);
+ ext4fs_devread((lbaint_t)blknr * fs->sect_perblk, 0, fs->blksz,
+ temp_buff);
+ jsb = (struct journal_superblock_t *)temp_buff;
+ jsb->s_start = 0;
+ put_ext4((uint64_t) ((uint64_t)blknr * (uint64_t)fs->blksz),
+ (struct journal_superblock_t *)temp_buff, fs->blksz);
+ free(temp_buff);
+ }
+ ext4fs_free_journal();
+
+ /* get the superblock */
+ ext4_read_superblock((char *)fs->sb);
+ new_feature_incompat = le32_to_cpu(fs->sb->feature_incompat);
+ new_feature_incompat &= ~EXT3_FEATURE_INCOMPAT_RECOVER;
+ fs->sb->feature_incompat = cpu_to_le32(new_feature_incompat);
+ put_ext4((uint64_t)(SUPERBLOCK_SIZE),
+ (struct ext2_sblock *)fs->sb, (uint32_t)SUPERBLOCK_SIZE);
+ free(fs->sb);
+ fs->sb = NULL;
+
+ if (fs->blk_bmaps) {
+ for (i = 0; i < fs->no_blkgrp; i++) {
+ free(fs->blk_bmaps[i]);
+ fs->blk_bmaps[i] = NULL;
+ }
+ free(fs->blk_bmaps);
+ fs->blk_bmaps = NULL;
+ }
+
+ if (fs->inode_bmaps) {
+ for (i = 0; i < fs->no_blkgrp; i++) {
+ free(fs->inode_bmaps[i]);
+ fs->inode_bmaps[i] = NULL;
+ }
+ free(fs->inode_bmaps);
+ fs->inode_bmaps = NULL;
+ }
+
+
+ free(fs->gdtable);
+ fs->gdtable = NULL;
+ /*
+ * reinitiliazed the global inode and
+ * block bitmap first execution check variables
+ */
+ fs->first_pass_ibmap = 0;
+ fs->first_pass_bbmap = 0;
+ fs->curr_inode_no = 0;
+ fs->curr_blkno = 0;
+}
+
+/*
+ * Write data to filesystem blocks. Uses same optimization for
+ * contigous sectors as ext4fs_read_file
+ */
+static int ext4fs_write_file(struct ext2_inode *file_inode,
+ int pos, unsigned int len, const char *buf)
+{
+ int i;
+ int blockcnt;
+ uint32_t filesize = le32_to_cpu(file_inode->size);
+ struct ext_filesystem *fs = get_fs();
+ int log2blksz = fs->dev_desc->log2blksz;
+ int log2_fs_blocksize = LOG2_BLOCK_SIZE(ext4fs_root) - log2blksz;
+ int previous_block_number = -1;
+ int delayed_start = 0;
+ int delayed_extent = 0;
+ int delayed_next = 0;
+ const char *delayed_buf = NULL;
+
+ /* Adjust len so it we can't read past the end of the file. */
+ if (len > filesize)
+ len = filesize;
+
+ blockcnt = ((len + pos) + fs->blksz - 1) / fs->blksz;
+
+ for (i = pos / fs->blksz; i < blockcnt; i++) {
+ long int blknr;
+ int blockend = fs->blksz;
+ int skipfirst = 0;
+ blknr = read_allocated_block(file_inode, i, NULL);
+ if (blknr <= 0)
+ return -1;
+
+ blknr = blknr << log2_fs_blocksize;
+
+ if (blknr) {
+ if (previous_block_number != -1) {
+ if (delayed_next == blknr) {
+ delayed_extent += blockend;
+ delayed_next += blockend >> log2blksz;
+ } else { /* spill */
+ put_ext4((uint64_t)
+ ((uint64_t)delayed_start << log2blksz),
+ delayed_buf,
+ (uint32_t) delayed_extent);
+ previous_block_number = blknr;
+ delayed_start = blknr;
+ delayed_extent = blockend;
+ delayed_buf = buf;
+ delayed_next = blknr +
+ (blockend >> log2blksz);
+ }
+ } else {
+ previous_block_number = blknr;
+ delayed_start = blknr;
+ delayed_extent = blockend;
+ delayed_buf = buf;
+ delayed_next = blknr +
+ (blockend >> log2blksz);
+ }
+ } else {
+ if (previous_block_number != -1) {
+ /* spill */
+ put_ext4((uint64_t) ((uint64_t)delayed_start <<
+ log2blksz),
+ delayed_buf,
+ (uint32_t) delayed_extent);
+ previous_block_number = -1;
+ }
+ }
+ buf += fs->blksz - skipfirst;
+ }
+ if (previous_block_number != -1) {
+ /* spill */
+ put_ext4((uint64_t) ((uint64_t)delayed_start << log2blksz),
+ delayed_buf, (uint32_t) delayed_extent);
+ previous_block_number = -1;
+ }
+
+ return len;
+}
+
+int ext4fs_write(const char *fname, const char *buffer,
+ unsigned long sizebytes, int type)
+{
+ int ret = 0;
+ struct ext2_inode *file_inode = NULL;
+ unsigned char *inode_buffer = NULL;
+ int parent_inodeno;
+ int inodeno;
+ time_t timestamp = 0;
+
+ uint64_t bytes_reqd_for_file;
+ unsigned int blks_reqd_for_file;
+ unsigned int blocks_remaining;
+ int existing_file_inodeno;
+ char *temp_ptr = NULL;
+ long int itable_blkno;
+ long int parent_itable_blkno;
+ long int blkoff;
+ struct ext2_sblock *sblock = &(ext4fs_root->sblock);
+ unsigned int inodes_per_block;
+ unsigned int ibmap_idx;
+ struct ext2_block_group *bgd = NULL;
+ struct ext_filesystem *fs = get_fs();
+ ALLOC_CACHE_ALIGN_BUFFER(char, filename, 256);
+ bool store_link_in_inode = false;
+ memset(filename, 0x00, 256);
+
+ if (type != FILETYPE_REG && type != FILETYPE_SYMLINK)
+ return -1;
+
+ g_parent_inode = zalloc(fs->inodesz);
+ if (!g_parent_inode)
+ goto fail;
+
+ if (ext4fs_init() != 0) {
+ printf("error in File System init\n");
+ return -1;
+ }
+
+ if (le32_to_cpu(fs->sb->feature_ro_compat) & EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) {
+ printf("Unsupported feature metadata_csum found, not writing.\n");
+ return -1;
+ }
+
+ inodes_per_block = fs->blksz / fs->inodesz;
+ parent_inodeno = ext4fs_get_parent_inode_num(fname, filename, F_FILE);
+ if (parent_inodeno == -1)
+ goto fail;
+ if (ext4fs_iget(parent_inodeno, g_parent_inode))
+ goto fail;
+ /* do not mess up a directory using hash trees */
+ if (le32_to_cpu(g_parent_inode->flags) & EXT4_INDEX_FL) {
+ printf("hash tree directory\n");
+ goto fail;
+ }
+ /* check if the filename is already present in root */
+ existing_file_inodeno = ext4fs_filename_unlink(filename);
+ if (existing_file_inodeno != -1) {
+ ret = ext4fs_delete_file(existing_file_inodeno);
+ fs->first_pass_bbmap = 0;
+ fs->curr_blkno = 0;
+
+ fs->first_pass_ibmap = 0;
+ fs->curr_inode_no = 0;
+ if (ret)
+ goto fail;
+ }
+
+ /* calculate how many blocks required */
+ if (type == FILETYPE_SYMLINK &&
+ sizebytes <= sizeof(file_inode->b.symlink)) {
+ store_link_in_inode = true;
+ bytes_reqd_for_file = 0;
+ } else {
+ bytes_reqd_for_file = sizebytes;
+ }
+
+ blks_reqd_for_file = lldiv(bytes_reqd_for_file, fs->blksz);
+ if (do_div(bytes_reqd_for_file, fs->blksz) != 0) {
+ blks_reqd_for_file++;
+ debug("total bytes for a file %u\n", blks_reqd_for_file);
+ }
+ blocks_remaining = blks_reqd_for_file;
+ /* test for available space in partition */
+ if (le32_to_cpu(fs->sb->free_blocks) < blks_reqd_for_file) {
+ printf("Not enough space on partition !!!\n");
+ goto fail;
+ }
+
+ inodeno = ext4fs_update_parent_dentry(filename, type);
+ if (inodeno == -1)
+ goto fail;
+ /* prepare file inode */
+ inode_buffer = zalloc(fs->inodesz);
+ if (!inode_buffer)
+ goto fail;
+ file_inode = (struct ext2_inode *)inode_buffer;
+ file_inode->size = cpu_to_le32(sizebytes);
+ if (type == FILETYPE_SYMLINK) {
+ file_inode->mode = cpu_to_le16(S_IFLNK | S_IRWXU | S_IRWXG |
+ S_IRWXO);
+ if (store_link_in_inode) {
+ strncpy(file_inode->b.symlink, buffer, sizebytes);
+ sizebytes = 0;
+ }
+ } else {
+ file_inode->mode = cpu_to_le16(S_IFREG | S_IRWXU | S_IRGRP |
+ S_IROTH | S_IXGRP | S_IXOTH);
+ }
+ /* ToDo: Update correct time */
+ file_inode->mtime = cpu_to_le32(timestamp);
+ file_inode->atime = cpu_to_le32(timestamp);
+ file_inode->ctime = cpu_to_le32(timestamp);
+ file_inode->nlinks = cpu_to_le16(1);
+
+ /* Allocate data blocks */
+ ext4fs_allocate_blocks(file_inode, blocks_remaining,
+ &blks_reqd_for_file);
+ file_inode->blockcnt = cpu_to_le32((blks_reqd_for_file * fs->blksz) >>
+ LOG2_SECTOR_SIZE);
+
+ temp_ptr = zalloc(fs->blksz);
+ if (!temp_ptr)
+ goto fail;
+ ibmap_idx = inodeno / le32_to_cpu(ext4fs_root->sblock.inodes_per_group);
+ inodeno--;
+ bgd = ext4fs_get_group_descriptor(fs, ibmap_idx);
+ itable_blkno = ext4fs_bg_get_inode_table_id(bgd, fs) +
+ (inodeno % le32_to_cpu(sblock->inodes_per_group)) /
+ inodes_per_block;
+ blkoff = (inodeno % inodes_per_block) * fs->inodesz;
+ ext4fs_devread((lbaint_t)itable_blkno * fs->sect_perblk, 0, fs->blksz,
+ temp_ptr);
+ if (ext4fs_log_journal(temp_ptr, itable_blkno))
+ goto fail;
+
+ memcpy(temp_ptr + blkoff, inode_buffer, fs->inodesz);
+ if (ext4fs_put_metadata(temp_ptr, itable_blkno))
+ goto fail;
+ /* copy the file content into data blocks */
+ if (ext4fs_write_file(file_inode, 0, sizebytes, buffer) == -1) {
+ printf("Error in copying content\n");
+ /* FIXME: Deallocate data blocks */
+ goto fail;
+ }
+ ibmap_idx = parent_inodeno / le32_to_cpu(ext4fs_root->sblock.inodes_per_group);
+ parent_inodeno--;
+ bgd = ext4fs_get_group_descriptor(fs, ibmap_idx);
+ parent_itable_blkno = ext4fs_bg_get_inode_table_id(bgd, fs) +
+ (parent_inodeno %
+ le32_to_cpu(sblock->inodes_per_group)) / inodes_per_block;
+ blkoff = (parent_inodeno % inodes_per_block) * fs->inodesz;
+ if (parent_itable_blkno != itable_blkno) {
+ memset(temp_ptr, '\0', fs->blksz);
+ ext4fs_devread((lbaint_t)parent_itable_blkno * fs->sect_perblk,
+ 0, fs->blksz, temp_ptr);
+ if (ext4fs_log_journal(temp_ptr, parent_itable_blkno))
+ goto fail;
+
+ memcpy(temp_ptr + blkoff, g_parent_inode, fs->inodesz);
+ if (ext4fs_put_metadata(temp_ptr, parent_itable_blkno))
+ goto fail;
+ } else {
+ /*
+ * If parent and child fall in same inode table block
+ * both should be kept in 1 buffer
+ */
+ memcpy(temp_ptr + blkoff, g_parent_inode, fs->inodesz);
+ gd_index--;
+ if (ext4fs_put_metadata(temp_ptr, itable_blkno))
+ goto fail;
+ }
+ ext4fs_update();
+ ext4fs_deinit();
+
+ fs->first_pass_bbmap = 0;
+ fs->curr_blkno = 0;
+ fs->first_pass_ibmap = 0;
+ fs->curr_inode_no = 0;
+ free(inode_buffer);
+ free(g_parent_inode);
+ free(temp_ptr);
+ g_parent_inode = NULL;
+
+ return 0;
+fail:
+ ext4fs_deinit();
+ free(inode_buffer);
+ free(g_parent_inode);
+ free(temp_ptr);
+ g_parent_inode = NULL;
+
+ return -1;
+}
+
+int ext4_write_file(const char *filename, void *buf, loff_t offset,
+ loff_t len, loff_t *actwrite)
+{
+ int ret;
+
+ if (offset != 0) {
+ printf("** Cannot support non-zero offset **\n");
+ return -1;
+ }
+
+ ret = ext4fs_write(filename, buf, len, FILETYPE_REG);
+ if (ret) {
+ printf("** Error ext4fs_write() **\n");
+ goto fail;
+ }
+
+ *actwrite = len;
+
+ return 0;
+
+fail:
+ *actwrite = 0;
+
+ return -1;
+}
+
+int ext4fs_create_link(const char *target, const char *fname)
+{
+ return ext4fs_write(fname, target, strlen(target), FILETYPE_SYMLINK);
+}
diff --git a/roms/u-boot/fs/ext4/ext4fs.c b/roms/u-boot/fs/ext4/ext4fs.c
new file mode 100644
index 000000000..4c89152ce
--- /dev/null
+++ b/roms/u-boot/fs/ext4/ext4fs.c
@@ -0,0 +1,304 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2011 - 2012 Samsung Electronics
+ * EXT4 filesystem implementation in Uboot by
+ * Uma Shankar <uma.shankar@samsung.com>
+ * Manjunatha C Achar <a.manjunatha@samsung.com>
+ *
+ * ext4ls and ext4load : Based on ext2 ls and load support in Uboot.
+ * Ext4 read optimization taken from Open-Moko
+ * Qi bootloader
+ *
+ * (C) Copyright 2004
+ * esd gmbh <www.esd-electronics.com>
+ * Reinhard Arlt <reinhard.arlt@esd-electronics.com>
+ *
+ * based on code from grub2 fs/ext2.c and fs/fshelp.c by
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+ *
+ * ext4write : Based on generic ext4 protocol.
+ */
+
+#include <common.h>
+#include <blk.h>
+#include <ext_common.h>
+#include <ext4fs.h>
+#include "ext4_common.h"
+#include <div64.h>
+#include <malloc.h>
+#include <part.h>
+#include <uuid.h>
+
+int ext4fs_symlinknest;
+struct ext_filesystem ext_fs;
+
+struct ext_filesystem *get_fs(void)
+{
+ return &ext_fs;
+}
+
+void ext4fs_free_node(struct ext2fs_node *node, struct ext2fs_node *currroot)
+{
+ if ((node != &ext4fs_root->diropen) && (node != currroot))
+ free(node);
+}
+
+/*
+ * Taken from openmoko-kernel mailing list: By Andy green
+ * Optimized read file API : collects and defers contiguous sector
+ * reads into one potentially more efficient larger sequential read action
+ */
+int ext4fs_read_file(struct ext2fs_node *node, loff_t pos,
+ loff_t len, char *buf, loff_t *actread)
+{
+ struct ext_filesystem *fs = get_fs();
+ int i;
+ lbaint_t blockcnt;
+ int log2blksz = fs->dev_desc->log2blksz;
+ int log2_fs_blocksize = LOG2_BLOCK_SIZE(node->data) - log2blksz;
+ int blocksize = (1 << (log2_fs_blocksize + log2blksz));
+ unsigned int filesize = le32_to_cpu(node->inode.size);
+ lbaint_t previous_block_number = -1;
+ lbaint_t delayed_start = 0;
+ lbaint_t delayed_extent = 0;
+ lbaint_t delayed_skipfirst = 0;
+ lbaint_t delayed_next = 0;
+ char *delayed_buf = NULL;
+ char *start_buf = buf;
+ short status;
+ struct ext_block_cache cache;
+
+ ext_cache_init(&cache);
+
+ /* Adjust len so it we can't read past the end of the file. */
+ if (len + pos > filesize)
+ len = (filesize - pos);
+
+ if (blocksize <= 0 || len <= 0) {
+ ext_cache_fini(&cache);
+ return -1;
+ }
+
+ blockcnt = lldiv(((len + pos) + blocksize - 1), blocksize);
+
+ for (i = lldiv(pos, blocksize); i < blockcnt; i++) {
+ long int blknr;
+ int blockoff = pos - (blocksize * i);
+ int blockend = blocksize;
+ int skipfirst = 0;
+ blknr = read_allocated_block(&node->inode, i, &cache);
+ if (blknr < 0) {
+ ext_cache_fini(&cache);
+ return -1;
+ }
+
+ blknr = blknr << log2_fs_blocksize;
+
+ /* Last block. */
+ if (i == blockcnt - 1) {
+ blockend = (len + pos) - (blocksize * i);
+
+ /* The last portion is exactly blocksize. */
+ if (!blockend)
+ blockend = blocksize;
+ }
+
+ /* First block. */
+ if (i == lldiv(pos, blocksize)) {
+ skipfirst = blockoff;
+ blockend -= skipfirst;
+ }
+ if (blknr) {
+ int status;
+
+ if (previous_block_number != -1) {
+ if (delayed_next == blknr) {
+ delayed_extent += blockend;
+ delayed_next += blockend >> log2blksz;
+ } else { /* spill */
+ status = ext4fs_devread(delayed_start,
+ delayed_skipfirst,
+ delayed_extent,
+ delayed_buf);
+ if (status == 0) {
+ ext_cache_fini(&cache);
+ return -1;
+ }
+ previous_block_number = blknr;
+ delayed_start = blknr;
+ delayed_extent = blockend;
+ delayed_skipfirst = skipfirst;
+ delayed_buf = buf;
+ delayed_next = blknr +
+ (blockend >> log2blksz);
+ }
+ } else {
+ previous_block_number = blknr;
+ delayed_start = blknr;
+ delayed_extent = blockend;
+ delayed_skipfirst = skipfirst;
+ delayed_buf = buf;
+ delayed_next = blknr +
+ (blockend >> log2blksz);
+ }
+ } else {
+ int n;
+ int n_left;
+ if (previous_block_number != -1) {
+ /* spill */
+ status = ext4fs_devread(delayed_start,
+ delayed_skipfirst,
+ delayed_extent,
+ delayed_buf);
+ if (status == 0) {
+ ext_cache_fini(&cache);
+ return -1;
+ }
+ previous_block_number = -1;
+ }
+ /* Zero no more than `len' bytes. */
+ n = blocksize - skipfirst;
+ n_left = len - ( buf - start_buf );
+ if (n > n_left)
+ n = n_left;
+ memset(buf, 0, n);
+ }
+ buf += blocksize - skipfirst;
+ }
+ if (previous_block_number != -1) {
+ /* spill */
+ status = ext4fs_devread(delayed_start,
+ delayed_skipfirst, delayed_extent,
+ delayed_buf);
+ if (status == 0) {
+ ext_cache_fini(&cache);
+ return -1;
+ }
+ previous_block_number = -1;
+ }
+
+ *actread = len;
+ ext_cache_fini(&cache);
+ return 0;
+}
+
+int ext4fs_ls(const char *dirname)
+{
+ struct ext2fs_node *dirnode = NULL;
+ int status;
+
+ if (dirname == NULL)
+ return 0;
+
+ status = ext4fs_find_file(dirname, &ext4fs_root->diropen, &dirnode,
+ FILETYPE_DIRECTORY);
+ if (status != 1) {
+ printf("** Can not find directory. **\n");
+ if (dirnode)
+ ext4fs_free_node(dirnode, &ext4fs_root->diropen);
+ return 1;
+ }
+
+ ext4fs_iterate_dir(dirnode, NULL, NULL, NULL);
+ ext4fs_free_node(dirnode, &ext4fs_root->diropen);
+
+ return 0;
+}
+
+int ext4fs_exists(const char *filename)
+{
+ loff_t file_len;
+ int ret;
+
+ ret = ext4fs_open(filename, &file_len);
+ return ret == 0;
+}
+
+int ext4fs_size(const char *filename, loff_t *size)
+{
+ return ext4fs_open(filename, size);
+}
+
+int ext4fs_read(char *buf, loff_t offset, loff_t len, loff_t *actread)
+{
+ if (ext4fs_root == NULL || ext4fs_file == NULL)
+ return -1;
+
+ return ext4fs_read_file(ext4fs_file, offset, len, buf, actread);
+}
+
+int ext4fs_probe(struct blk_desc *fs_dev_desc,
+ struct disk_partition *fs_partition)
+{
+ ext4fs_set_blk_dev(fs_dev_desc, fs_partition);
+
+ if (!ext4fs_mount(fs_partition->size)) {
+ ext4fs_close();
+ return -1;
+ }
+
+ return 0;
+}
+
+int ext4_read_file(const char *filename, void *buf, loff_t offset, loff_t len,
+ loff_t *len_read)
+{
+ loff_t file_len;
+ int ret;
+
+ ret = ext4fs_open(filename, &file_len);
+ if (ret < 0) {
+ printf("** File not found %s **\n", filename);
+ return -1;
+ }
+
+ if (len == 0)
+ len = file_len;
+
+ return ext4fs_read(buf, offset, len, len_read);
+}
+
+int ext4fs_uuid(char *uuid_str)
+{
+ if (ext4fs_root == NULL)
+ return -1;
+
+#ifdef CONFIG_LIB_UUID
+ uuid_bin_to_str((unsigned char *)ext4fs_root->sblock.unique_id,
+ uuid_str, UUID_STR_FORMAT_STD);
+
+ return 0;
+#else
+ return -ENOSYS;
+#endif
+}
+
+void ext_cache_init(struct ext_block_cache *cache)
+{
+ memset(cache, 0, sizeof(*cache));
+}
+
+void ext_cache_fini(struct ext_block_cache *cache)
+{
+ free(cache->buf);
+ ext_cache_init(cache);
+}
+
+int ext_cache_read(struct ext_block_cache *cache, lbaint_t block, int size)
+{
+ /* This could be more lenient, but this is simple and enough for now */
+ if (cache->buf && cache->block == block && cache->size == size)
+ return 1;
+ ext_cache_fini(cache);
+ cache->buf = memalign(ARCH_DMA_MINALIGN, size);
+ if (!cache->buf)
+ return 0;
+ if (!ext4fs_devread(block, 0, size, cache->buf)) {
+ ext_cache_fini(cache);
+ return 0;
+ }
+ cache->block = block;
+ cache->size = size;
+ return 1;
+}
diff --git a/roms/u-boot/fs/fat/Kconfig b/roms/u-boot/fs/fat/Kconfig
new file mode 100644
index 000000000..9bb11eac9
--- /dev/null
+++ b/roms/u-boot/fs/fat/Kconfig
@@ -0,0 +1,24 @@
+config FS_FAT
+ bool "Enable FAT filesystem support"
+ help
+ This provides support for reading images from File Allocation Table
+ (FAT) filesystem. FAT filesystem is a legacy, lightweight filesystem.
+ It is useful mainly for its wide compatibility with various operating
+ systems. You can also enable CMD_FAT to get access to fat commands.
+
+config FAT_WRITE
+ bool "Enable FAT filesystem write support"
+ depends on FS_FAT
+ help
+ This provides support for creating and writing new files to an
+ existing FAT filesystem partition.
+
+config FS_FAT_MAX_CLUSTSIZE
+ int "Set maximum possible clustersize"
+ default 65536
+ depends on FS_FAT
+ help
+ Set the maximum possible clustersize for the FAT filesytem. This
+ is the smallest amount of disk space that can be used to hold a
+ file. Unless you have an extremely tight memory memory constraints,
+ leave the default.
diff --git a/roms/u-boot/fs/fat/Makefile b/roms/u-boot/fs/fat/Makefile
new file mode 100644
index 000000000..f84efaccc
--- /dev/null
+++ b/roms/u-boot/fs/fat/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+
+obj-$(CONFIG_$(SPL_)FS_FAT) = fat.o
+obj-$(CONFIG_$(SPL_)FAT_WRITE) = fat_write.o
diff --git a/roms/u-boot/fs/fat/fat.c b/roms/u-boot/fs/fat/fat.c
new file mode 100644
index 000000000..c561d82b3
--- /dev/null
+++ b/roms/u-boot/fs/fat/fat.c
@@ -0,0 +1,1368 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * fat.c
+ *
+ * R/O (V)FAT 12/16/32 filesystem implementation by Marcus Sundberg
+ *
+ * 2002-07-28 - rjones@nexus-tech.net - ported to ppcboot v1.1.6
+ * 2003-03-10 - kharris@nexus-tech.net - ported to uboot
+ */
+
+#include <common.h>
+#include <blk.h>
+#include <config.h>
+#include <exports.h>
+#include <fat.h>
+#include <fs.h>
+#include <log.h>
+#include <asm/byteorder.h>
+#include <part.h>
+#include <malloc.h>
+#include <memalign.h>
+#include <asm/cache.h>
+#include <linux/compiler.h>
+#include <linux/ctype.h>
+
+/*
+ * Convert a string to lowercase. Converts at most 'len' characters,
+ * 'len' may be larger than the length of 'str' if 'str' is NULL
+ * terminated.
+ */
+static void downcase(char *str, size_t len)
+{
+ while (*str != '\0' && len--) {
+ *str = tolower(*str);
+ str++;
+ }
+}
+
+static struct blk_desc *cur_dev;
+static struct disk_partition cur_part_info;
+
+#define DOS_BOOT_MAGIC_OFFSET 0x1fe
+#define DOS_FS_TYPE_OFFSET 0x36
+#define DOS_FS32_TYPE_OFFSET 0x52
+
+static int disk_read(__u32 block, __u32 nr_blocks, void *buf)
+{
+ ulong ret;
+
+ if (!cur_dev)
+ return -1;
+
+ ret = blk_dread(cur_dev, cur_part_info.start + block, nr_blocks, buf);
+
+ if (ret != nr_blocks)
+ return -1;
+
+ return ret;
+}
+
+int fat_set_blk_dev(struct blk_desc *dev_desc, struct disk_partition *info)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, dev_desc->blksz);
+
+ cur_dev = dev_desc;
+ cur_part_info = *info;
+
+ /* Make sure it has a valid FAT header */
+ if (disk_read(0, 1, buffer) != 1) {
+ cur_dev = NULL;
+ return -1;
+ }
+
+ /* Check if it's actually a DOS volume */
+ if (memcmp(buffer + DOS_BOOT_MAGIC_OFFSET, "\x55\xAA", 2)) {
+ cur_dev = NULL;
+ return -1;
+ }
+
+ /* Check for FAT12/FAT16/FAT32 filesystem */
+ if (!memcmp(buffer + DOS_FS_TYPE_OFFSET, "FAT", 3))
+ return 0;
+ if (!memcmp(buffer + DOS_FS32_TYPE_OFFSET, "FAT32", 5))
+ return 0;
+
+ cur_dev = NULL;
+ return -1;
+}
+
+int fat_register_device(struct blk_desc *dev_desc, int part_no)
+{
+ struct disk_partition info;
+
+ /* First close any currently found FAT filesystem */
+ cur_dev = NULL;
+
+ /* Read the partition table, if present */
+ if (part_get_info(dev_desc, part_no, &info)) {
+ if (part_no != 0) {
+ printf("** Partition %d not valid on device %d **\n",
+ part_no, dev_desc->devnum);
+ return -1;
+ }
+
+ info.start = 0;
+ info.size = dev_desc->lba;
+ info.blksz = dev_desc->blksz;
+ info.name[0] = 0;
+ info.type[0] = 0;
+ info.bootable = 0;
+#if CONFIG_IS_ENABLED(PARTITION_UUIDS)
+ info.uuid[0] = 0;
+#endif
+ }
+
+ return fat_set_blk_dev(dev_desc, &info);
+}
+
+/*
+ * Extract zero terminated short name from a directory entry.
+ */
+static void get_name(dir_entry *dirent, char *s_name)
+{
+ char *ptr;
+
+ memcpy(s_name, dirent->nameext.name, 8);
+ s_name[8] = '\0';
+ ptr = s_name;
+ while (*ptr && *ptr != ' ')
+ ptr++;
+ if (dirent->lcase & CASE_LOWER_BASE)
+ downcase(s_name, (unsigned)(ptr - s_name));
+ if (dirent->nameext.ext[0] && dirent->nameext.ext[0] != ' ') {
+ *ptr++ = '.';
+ memcpy(ptr, dirent->nameext.ext, 3);
+ if (dirent->lcase & CASE_LOWER_EXT)
+ downcase(ptr, 3);
+ ptr[3] = '\0';
+ while (*ptr && *ptr != ' ')
+ ptr++;
+ }
+ *ptr = '\0';
+ if (*s_name == DELETED_FLAG)
+ *s_name = '\0';
+ else if (*s_name == aRING)
+ *s_name = DELETED_FLAG;
+}
+
+static int flush_dirty_fat_buffer(fsdata *mydata);
+
+#if !CONFIG_IS_ENABLED(FAT_WRITE)
+/* Stub for read only operation */
+int flush_dirty_fat_buffer(fsdata *mydata)
+{
+ (void)(mydata);
+ return 0;
+}
+#endif
+
+/*
+ * Get the entry at index 'entry' in a FAT (12/16/32) table.
+ * On failure 0x00 is returned.
+ */
+static __u32 get_fatent(fsdata *mydata, __u32 entry)
+{
+ __u32 bufnum;
+ __u32 offset, off8;
+ __u32 ret = 0x00;
+
+ if (CHECK_CLUST(entry, mydata->fatsize)) {
+ printf("Error: Invalid FAT entry: 0x%08x\n", entry);
+ return ret;
+ }
+
+ switch (mydata->fatsize) {
+ case 32:
+ bufnum = entry / FAT32BUFSIZE;
+ offset = entry - bufnum * FAT32BUFSIZE;
+ break;
+ case 16:
+ bufnum = entry / FAT16BUFSIZE;
+ offset = entry - bufnum * FAT16BUFSIZE;
+ break;
+ case 12:
+ bufnum = entry / FAT12BUFSIZE;
+ offset = entry - bufnum * FAT12BUFSIZE;
+ break;
+
+ default:
+ /* Unsupported FAT size */
+ return ret;
+ }
+
+ debug("FAT%d: entry: 0x%08x = %d, offset: 0x%04x = %d\n",
+ mydata->fatsize, entry, entry, offset, offset);
+
+ /* Read a new block of FAT entries into the cache. */
+ if (bufnum != mydata->fatbufnum) {
+ __u32 getsize = FATBUFBLOCKS;
+ __u8 *bufptr = mydata->fatbuf;
+ __u32 fatlength = mydata->fatlength;
+ __u32 startblock = bufnum * FATBUFBLOCKS;
+
+ /* Cap length if fatlength is not a multiple of FATBUFBLOCKS */
+ if (startblock + getsize > fatlength)
+ getsize = fatlength - startblock;
+
+ startblock += mydata->fat_sect; /* Offset from start of disk */
+
+ /* Write back the fatbuf to the disk */
+ if (flush_dirty_fat_buffer(mydata) < 0)
+ return -1;
+
+ if (disk_read(startblock, getsize, bufptr) < 0) {
+ debug("Error reading FAT blocks\n");
+ return ret;
+ }
+ mydata->fatbufnum = bufnum;
+ }
+
+ /* Get the actual entry from the table */
+ switch (mydata->fatsize) {
+ case 32:
+ ret = FAT2CPU32(((__u32 *) mydata->fatbuf)[offset]);
+ break;
+ case 16:
+ ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[offset]);
+ break;
+ case 12:
+ off8 = (offset * 3) / 2;
+ /* fatbut + off8 may be unaligned, read in byte granularity */
+ ret = mydata->fatbuf[off8] + (mydata->fatbuf[off8 + 1] << 8);
+
+ if (offset & 0x1)
+ ret >>= 4;
+ ret &= 0xfff;
+ }
+ debug("FAT%d: ret: 0x%08x, entry: 0x%08x, offset: 0x%04x\n",
+ mydata->fatsize, ret, entry, offset);
+
+ return ret;
+}
+
+/*
+ * Read at most 'size' bytes from the specified cluster into 'buffer'.
+ * Return 0 on success, -1 otherwise.
+ */
+static int
+get_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer, unsigned long size)
+{
+ __u32 startsect;
+ int ret;
+
+ if (clustnum > 0) {
+ startsect = clust_to_sect(mydata, clustnum);
+ } else {
+ startsect = mydata->rootdir_sect;
+ }
+
+ debug("gc - clustnum: %d, startsect: %d\n", clustnum, startsect);
+
+ if ((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1)) {
+ ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
+
+ debug("FAT: Misaligned buffer address (%p)\n", buffer);
+
+ while (size >= mydata->sect_size) {
+ ret = disk_read(startsect++, 1, tmpbuf);
+ if (ret != 1) {
+ debug("Error reading data (got %d)\n", ret);
+ return -1;
+ }
+
+ memcpy(buffer, tmpbuf, mydata->sect_size);
+ buffer += mydata->sect_size;
+ size -= mydata->sect_size;
+ }
+ } else {
+ __u32 idx;
+
+ idx = size / mydata->sect_size;
+ if (idx == 0)
+ ret = 0;
+ else
+ ret = disk_read(startsect, idx, buffer);
+ if (ret != idx) {
+ debug("Error reading data (got %d)\n", ret);
+ return -1;
+ }
+ startsect += idx;
+ idx *= mydata->sect_size;
+ buffer += idx;
+ size -= idx;
+ }
+ if (size) {
+ ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
+
+ ret = disk_read(startsect, 1, tmpbuf);
+ if (ret != 1) {
+ debug("Error reading data (got %d)\n", ret);
+ return -1;
+ }
+
+ memcpy(buffer, tmpbuf, size);
+ }
+
+ return 0;
+}
+
+/**
+ * get_contents() - read from file
+ *
+ * Read at most 'maxsize' bytes from 'pos' in the file associated with 'dentptr'
+ * into 'buffer'. Update the number of bytes read in *gotsize or return -1 on
+ * fatal errors.
+ *
+ * @mydata: file system description
+ * @dentprt: directory entry pointer
+ * @pos: position from where to read
+ * @buffer: buffer into which to read
+ * @maxsize: maximum number of bytes to read
+ * @gotsize: number of bytes actually read
+ * Return: -1 on error, otherwise 0
+ */
+static int get_contents(fsdata *mydata, dir_entry *dentptr, loff_t pos,
+ __u8 *buffer, loff_t maxsize, loff_t *gotsize)
+{
+ loff_t filesize = FAT2CPU32(dentptr->size);
+ unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
+ __u32 curclust = START(dentptr);
+ __u32 endclust, newclust;
+ loff_t actsize;
+
+ *gotsize = 0;
+ debug("Filesize: %llu bytes\n", filesize);
+
+ if (pos >= filesize) {
+ debug("Read position past EOF: %llu\n", pos);
+ return 0;
+ }
+
+ if (maxsize > 0 && filesize > pos + maxsize)
+ filesize = pos + maxsize;
+
+ debug("%llu bytes\n", filesize);
+
+ actsize = bytesperclust;
+
+ /* go to cluster at pos */
+ while (actsize <= pos) {
+ curclust = get_fatent(mydata, curclust);
+ if (CHECK_CLUST(curclust, mydata->fatsize)) {
+ debug("curclust: 0x%x\n", curclust);
+ printf("Invalid FAT entry\n");
+ return -1;
+ }
+ actsize += bytesperclust;
+ }
+
+ /* actsize > pos */
+ actsize -= bytesperclust;
+ filesize -= actsize;
+ pos -= actsize;
+
+ /* align to beginning of next cluster if any */
+ if (pos) {
+ __u8 *tmp_buffer;
+
+ actsize = min(filesize, (loff_t)bytesperclust);
+ tmp_buffer = malloc_cache_aligned(actsize);
+ if (!tmp_buffer) {
+ debug("Error: allocating buffer\n");
+ return -1;
+ }
+
+ if (get_cluster(mydata, curclust, tmp_buffer, actsize) != 0) {
+ printf("Error reading cluster\n");
+ free(tmp_buffer);
+ return -1;
+ }
+ filesize -= actsize;
+ actsize -= pos;
+ memcpy(buffer, tmp_buffer + pos, actsize);
+ free(tmp_buffer);
+ *gotsize += actsize;
+ if (!filesize)
+ return 0;
+ buffer += actsize;
+
+ curclust = get_fatent(mydata, curclust);
+ if (CHECK_CLUST(curclust, mydata->fatsize)) {
+ debug("curclust: 0x%x\n", curclust);
+ printf("Invalid FAT entry\n");
+ return -1;
+ }
+ }
+
+ actsize = bytesperclust;
+ endclust = curclust;
+
+ do {
+ /* search for consecutive clusters */
+ while (actsize < filesize) {
+ newclust = get_fatent(mydata, endclust);
+ if ((newclust - 1) != endclust)
+ goto getit;
+ if (CHECK_CLUST(newclust, mydata->fatsize)) {
+ debug("curclust: 0x%x\n", newclust);
+ printf("Invalid FAT entry\n");
+ return -1;
+ }
+ endclust = newclust;
+ actsize += bytesperclust;
+ }
+
+ /* get remaining bytes */
+ actsize = filesize;
+ if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
+ printf("Error reading cluster\n");
+ return -1;
+ }
+ *gotsize += actsize;
+ return 0;
+getit:
+ if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
+ printf("Error reading cluster\n");
+ return -1;
+ }
+ *gotsize += (int)actsize;
+ filesize -= actsize;
+ buffer += actsize;
+
+ curclust = get_fatent(mydata, endclust);
+ if (CHECK_CLUST(curclust, mydata->fatsize)) {
+ debug("curclust: 0x%x\n", curclust);
+ printf("Invalid FAT entry\n");
+ return -1;
+ }
+ actsize = bytesperclust;
+ endclust = curclust;
+ } while (1);
+}
+
+/*
+ * Extract the file name information from 'slotptr' into 'l_name',
+ * starting at l_name[*idx].
+ * Return 1 if terminator (zero byte) is found, 0 otherwise.
+ */
+static int slot2str(dir_slot *slotptr, char *l_name, int *idx)
+{
+ int j;
+
+ for (j = 0; j <= 8; j += 2) {
+ l_name[*idx] = slotptr->name0_4[j];
+ if (l_name[*idx] == 0x00)
+ return 1;
+ (*idx)++;
+ }
+ for (j = 0; j <= 10; j += 2) {
+ l_name[*idx] = slotptr->name5_10[j];
+ if (l_name[*idx] == 0x00)
+ return 1;
+ (*idx)++;
+ }
+ for (j = 0; j <= 2; j += 2) {
+ l_name[*idx] = slotptr->name11_12[j];
+ if (l_name[*idx] == 0x00)
+ return 1;
+ (*idx)++;
+ }
+
+ return 0;
+}
+
+/* Calculate short name checksum */
+static __u8 mkcksum(struct nameext *nameext)
+{
+ int i;
+ u8 *pos = (void *)nameext;
+
+ __u8 ret = 0;
+
+ for (i = 0; i < 11; i++)
+ ret = (((ret & 1) << 7) | ((ret & 0xfe) >> 1)) + pos[i];
+
+ return ret;
+}
+
+/*
+ * Read boot sector and volume info from a FAT filesystem
+ */
+static int
+read_bootsectandvi(boot_sector *bs, volume_info *volinfo, int *fatsize)
+{
+ __u8 *block;
+ volume_info *vistart;
+ int ret = 0;
+
+ if (cur_dev == NULL) {
+ debug("Error: no device selected\n");
+ return -1;
+ }
+
+ block = malloc_cache_aligned(cur_dev->blksz);
+ if (block == NULL) {
+ debug("Error: allocating block\n");
+ return -1;
+ }
+
+ if (disk_read(0, 1, block) < 0) {
+ debug("Error: reading block\n");
+ goto fail;
+ }
+
+ memcpy(bs, block, sizeof(boot_sector));
+ bs->reserved = FAT2CPU16(bs->reserved);
+ bs->fat_length = FAT2CPU16(bs->fat_length);
+ bs->secs_track = FAT2CPU16(bs->secs_track);
+ bs->heads = FAT2CPU16(bs->heads);
+ bs->total_sect = FAT2CPU32(bs->total_sect);
+
+ /* FAT32 entries */
+ if (bs->fat_length == 0) {
+ /* Assume FAT32 */
+ bs->fat32_length = FAT2CPU32(bs->fat32_length);
+ bs->flags = FAT2CPU16(bs->flags);
+ bs->root_cluster = FAT2CPU32(bs->root_cluster);
+ bs->info_sector = FAT2CPU16(bs->info_sector);
+ bs->backup_boot = FAT2CPU16(bs->backup_boot);
+ vistart = (volume_info *)(block + sizeof(boot_sector));
+ *fatsize = 32;
+ } else {
+ vistart = (volume_info *)&(bs->fat32_length);
+ *fatsize = 0;
+ }
+ memcpy(volinfo, vistart, sizeof(volume_info));
+
+ if (*fatsize == 32) {
+ if (strncmp(FAT32_SIGN, vistart->fs_type, SIGNLEN) == 0)
+ goto exit;
+ } else {
+ if (strncmp(FAT12_SIGN, vistart->fs_type, SIGNLEN) == 0) {
+ *fatsize = 12;
+ goto exit;
+ }
+ if (strncmp(FAT16_SIGN, vistart->fs_type, SIGNLEN) == 0) {
+ *fatsize = 16;
+ goto exit;
+ }
+ }
+
+ debug("Error: broken fs_type sign\n");
+fail:
+ ret = -1;
+exit:
+ free(block);
+ return ret;
+}
+
+static int get_fs_info(fsdata *mydata)
+{
+ boot_sector bs;
+ volume_info volinfo;
+ int ret;
+
+ ret = read_bootsectandvi(&bs, &volinfo, &mydata->fatsize);
+ if (ret) {
+ debug("Error: reading boot sector\n");
+ return ret;
+ }
+
+ if (mydata->fatsize == 32) {
+ mydata->fatlength = bs.fat32_length;
+ mydata->total_sect = bs.total_sect;
+ } else {
+ mydata->fatlength = bs.fat_length;
+ mydata->total_sect = (bs.sectors[1] << 8) + bs.sectors[0];
+ if (!mydata->total_sect)
+ mydata->total_sect = bs.total_sect;
+ }
+ if (!mydata->total_sect) /* unlikely */
+ mydata->total_sect = (u32)cur_part_info.size;
+
+ mydata->fats = bs.fats;
+ mydata->fat_sect = bs.reserved;
+
+ mydata->rootdir_sect = mydata->fat_sect + mydata->fatlength * bs.fats;
+
+ mydata->sect_size = (bs.sector_size[1] << 8) + bs.sector_size[0];
+ mydata->clust_size = bs.cluster_size;
+ if (mydata->sect_size != cur_part_info.blksz) {
+ printf("Error: FAT sector size mismatch (fs=%hu, dev=%lu)\n",
+ mydata->sect_size, cur_part_info.blksz);
+ return -1;
+ }
+ if (mydata->clust_size == 0) {
+ printf("Error: FAT cluster size not set\n");
+ return -1;
+ }
+ if ((unsigned int)mydata->clust_size * mydata->sect_size >
+ MAX_CLUSTSIZE) {
+ printf("Error: FAT cluster size too big (cs=%u, max=%u)\n",
+ (unsigned int)mydata->clust_size * mydata->sect_size,
+ MAX_CLUSTSIZE);
+ return -1;
+ }
+
+ if (mydata->fatsize == 32) {
+ mydata->data_begin = mydata->rootdir_sect -
+ (mydata->clust_size * 2);
+ mydata->root_cluster = bs.root_cluster;
+ } else {
+ mydata->rootdir_size = ((bs.dir_entries[1] * (int)256 +
+ bs.dir_entries[0]) *
+ sizeof(dir_entry)) /
+ mydata->sect_size;
+ mydata->data_begin = mydata->rootdir_sect +
+ mydata->rootdir_size -
+ (mydata->clust_size * 2);
+
+ /*
+ * The root directory is not cluster-aligned and may be on a
+ * "negative" cluster, this will be handled specially in
+ * fat_next_cluster().
+ */
+ mydata->root_cluster = 0;
+ }
+
+ mydata->fatbufnum = -1;
+ mydata->fat_dirty = 0;
+ mydata->fatbuf = malloc_cache_aligned(FATBUFSIZE);
+ if (mydata->fatbuf == NULL) {
+ debug("Error: allocating memory\n");
+ return -1;
+ }
+
+ debug("FAT%d, fat_sect: %d, fatlength: %d\n",
+ mydata->fatsize, mydata->fat_sect, mydata->fatlength);
+ debug("Rootdir begins at cluster: %d, sector: %d, offset: %x\n"
+ "Data begins at: %d\n",
+ mydata->root_cluster,
+ mydata->rootdir_sect,
+ mydata->rootdir_sect * mydata->sect_size, mydata->data_begin);
+ debug("Sector size: %d, cluster size: %d\n", mydata->sect_size,
+ mydata->clust_size);
+
+ return 0;
+}
+
+/**
+ * struct fat_itr - directory iterator, to simplify filesystem traversal
+ *
+ * Implements an iterator pattern to traverse directory tables,
+ * transparently handling directory tables split across multiple
+ * clusters, and the difference between FAT12/FAT16 root directory
+ * (contiguous) and subdirectories + FAT32 root (chained).
+ *
+ * Rough usage
+ *
+ * .. code-block:: c
+ *
+ * for (fat_itr_root(&itr, fsdata); fat_itr_next(&itr); ) {
+ * // to traverse down to a subdirectory pointed to by
+ * // current iterator position:
+ * fat_itr_child(&itr, &itr);
+ * }
+ *
+ * For a more complete example, see fat_itr_resolve().
+ */
+struct fat_itr {
+ /**
+ * @fsdata: filesystem parameters
+ */
+ fsdata *fsdata;
+ /**
+ * @start_clust: first cluster
+ */
+ unsigned int start_clust;
+ /**
+ * @clust: current cluster
+ */
+ unsigned int clust;
+ /**
+ * @next_clust: next cluster if remaining == 0
+ */
+ unsigned int next_clust;
+ /**
+ * @last_cluster: set if last cluster of directory reached
+ */
+ int last_cluster;
+ /**
+ * @is_root: is iterator at root directory
+ */
+ int is_root;
+ /**
+ * @remaining: remaining directory entries in current cluster
+ */
+ int remaining;
+ /**
+ * @dent: current directory entry
+ */
+ dir_entry *dent;
+ /**
+ * @dent_rem: remaining entries after long name start
+ */
+ int dent_rem;
+ /**
+ * @dent_clust: cluster of long name start
+ */
+ unsigned int dent_clust;
+ /**
+ * @dent_start: first directory entry for long name
+ */
+ dir_entry *dent_start;
+ /**
+ * @l_name: long name of current directory entry
+ */
+ char l_name[VFAT_MAXLEN_BYTES];
+ /**
+ * @s_name: short 8.3 name of current directory entry
+ */
+ char s_name[14];
+ /**
+ * @name: l_name if there is one, else s_name
+ */
+ char *name;
+ /**
+ * @block: buffer for current cluster
+ */
+ u8 block[MAX_CLUSTSIZE] __aligned(ARCH_DMA_MINALIGN);
+};
+
+static int fat_itr_isdir(fat_itr *itr);
+
+/**
+ * fat_itr_root() - initialize an iterator to start at the root
+ * directory
+ *
+ * @itr: iterator to initialize
+ * @fsdata: filesystem data for the partition
+ * @return 0 on success, else -errno
+ */
+static int fat_itr_root(fat_itr *itr, fsdata *fsdata)
+{
+ if (get_fs_info(fsdata))
+ return -ENXIO;
+
+ itr->fsdata = fsdata;
+ itr->start_clust = fsdata->root_cluster;
+ itr->clust = fsdata->root_cluster;
+ itr->next_clust = fsdata->root_cluster;
+ itr->dent = NULL;
+ itr->remaining = 0;
+ itr->last_cluster = 0;
+ itr->is_root = 1;
+
+ return 0;
+}
+
+/**
+ * fat_itr_child() - initialize an iterator to descend into a sub-
+ * directory
+ *
+ * Initializes 'itr' to iterate the contents of the directory at
+ * the current cursor position of 'parent'. It is an error to
+ * call this if the current cursor of 'parent' is pointing at a
+ * regular file.
+ *
+ * Note that 'itr' and 'parent' can be the same pointer if you do
+ * not need to preserve 'parent' after this call, which is useful
+ * for traversing directory structure to resolve a file/directory.
+ *
+ * @itr: iterator to initialize
+ * @parent: the iterator pointing at a directory entry in the
+ * parent directory of the directory to iterate
+ */
+static void fat_itr_child(fat_itr *itr, fat_itr *parent)
+{
+ fsdata *mydata = parent->fsdata; /* for silly macros */
+ unsigned clustnum = START(parent->dent);
+
+ assert(fat_itr_isdir(parent));
+
+ itr->fsdata = parent->fsdata;
+ itr->start_clust = clustnum;
+ if (clustnum > 0) {
+ itr->clust = clustnum;
+ itr->next_clust = clustnum;
+ itr->is_root = 0;
+ } else {
+ itr->clust = parent->fsdata->root_cluster;
+ itr->next_clust = parent->fsdata->root_cluster;
+ itr->start_clust = parent->fsdata->root_cluster;
+ itr->is_root = 1;
+ }
+ itr->dent = NULL;
+ itr->remaining = 0;
+ itr->last_cluster = 0;
+}
+
+/**
+ * fat_next_cluster() - load next FAT cluster
+ *
+ * The function is used when iterating through directories. It loads the
+ * next cluster with directory entries
+ *
+ * @itr: directory iterator
+ * @nbytes: number of bytes read, 0 on error
+ * Return: first directory entry, NULL on error
+ */
+void *fat_next_cluster(fat_itr *itr, unsigned int *nbytes)
+{
+ int ret;
+ u32 sect;
+ u32 read_size;
+
+ /* have we reached the end? */
+ if (itr->last_cluster)
+ return NULL;
+
+ if (itr->is_root && itr->fsdata->fatsize != 32) {
+ /*
+ * The root directory is located before the data area and
+ * cannot be indexed using the regular unsigned cluster
+ * numbers (it may start at a "negative" cluster or not at a
+ * cluster boundary at all), so consider itr->next_clust to be
+ * a offset in cluster-sized units from the start of rootdir.
+ */
+ unsigned sect_offset = itr->next_clust * itr->fsdata->clust_size;
+ unsigned remaining_sects = itr->fsdata->rootdir_size - sect_offset;
+ sect = itr->fsdata->rootdir_sect + sect_offset;
+ /* do not read past the end of rootdir */
+ read_size = min_t(u32, itr->fsdata->clust_size,
+ remaining_sects);
+ } else {
+ sect = clust_to_sect(itr->fsdata, itr->next_clust);
+ read_size = itr->fsdata->clust_size;
+ }
+
+ log_debug("FAT read(sect=%d), clust_size=%d, read_size=%u\n",
+ sect, itr->fsdata->clust_size, read_size);
+
+ /*
+ * NOTE: do_fat_read_at() had complicated logic to deal w/
+ * vfat names that span multiple clusters in the fat16 case,
+ * which get_dentfromdir() probably also needed (and was
+ * missing). And not entirely sure what fat32 didn't have
+ * the same issue.. We solve that by only caring about one
+ * dent at a time and iteratively constructing the vfat long
+ * name.
+ */
+ ret = disk_read(sect, read_size, itr->block);
+ if (ret < 0) {
+ debug("Error: reading block\n");
+ return NULL;
+ }
+
+ *nbytes = read_size * itr->fsdata->sect_size;
+ itr->clust = itr->next_clust;
+ if (itr->is_root && itr->fsdata->fatsize != 32) {
+ itr->next_clust++;
+ if (itr->next_clust * itr->fsdata->clust_size >=
+ itr->fsdata->rootdir_size) {
+ debug("nextclust: 0x%x\n", itr->next_clust);
+ itr->last_cluster = 1;
+ }
+ } else {
+ itr->next_clust = get_fatent(itr->fsdata, itr->next_clust);
+ if (CHECK_CLUST(itr->next_clust, itr->fsdata->fatsize)) {
+ debug("nextclust: 0x%x\n", itr->next_clust);
+ itr->last_cluster = 1;
+ }
+ }
+
+ return itr->block;
+}
+
+static dir_entry *next_dent(fat_itr *itr)
+{
+ if (itr->remaining == 0) {
+ unsigned nbytes;
+ struct dir_entry *dent = fat_next_cluster(itr, &nbytes);
+
+ /* have we reached the last cluster? */
+ if (!dent) {
+ /* a sign for no more entries left */
+ itr->dent = NULL;
+ return NULL;
+ }
+
+ itr->remaining = nbytes / sizeof(dir_entry) - 1;
+ itr->dent = dent;
+ } else {
+ itr->remaining--;
+ itr->dent++;
+ }
+
+ /* have we reached the last valid entry? */
+ if (itr->dent->nameext.name[0] == 0)
+ return NULL;
+
+ return itr->dent;
+}
+
+static dir_entry *extract_vfat_name(fat_itr *itr)
+{
+ struct dir_entry *dent = itr->dent;
+ int seqn = itr->dent->nameext.name[0] & ~LAST_LONG_ENTRY_MASK;
+ u8 chksum, alias_checksum = ((dir_slot *)dent)->alias_checksum;
+ int n = 0;
+
+ while (seqn--) {
+ char buf[13];
+ int idx = 0;
+
+ slot2str((dir_slot *)dent, buf, &idx);
+
+ if (n + idx >= sizeof(itr->l_name))
+ return NULL;
+
+ /* shift accumulated long-name up and copy new part in: */
+ memmove(itr->l_name + idx, itr->l_name, n);
+ memcpy(itr->l_name, buf, idx);
+ n += idx;
+
+ dent = next_dent(itr);
+ if (!dent)
+ return NULL;
+ }
+
+ /*
+ * We are now at the short file name entry.
+ * If it is marked as deleted, just skip it.
+ */
+ if (dent->nameext.name[0] == DELETED_FLAG ||
+ dent->nameext.name[0] == aRING)
+ return NULL;
+
+ itr->l_name[n] = '\0';
+
+ chksum = mkcksum(&dent->nameext);
+
+ /* checksum mismatch could mean deleted file, etc.. skip it: */
+ if (chksum != alias_checksum) {
+ debug("** chksum=%x, alias_checksum=%x, l_name=%s, s_name=%8s.%3s\n",
+ chksum, alias_checksum, itr->l_name, dent->nameext.name,
+ dent->nameext.ext);
+ return NULL;
+ }
+
+ return dent;
+}
+
+/**
+ * fat_itr_next() - step to the next entry in a directory
+ *
+ * Must be called once on a new iterator before the cursor is valid.
+ *
+ * @itr: the iterator to iterate
+ * @return boolean, 1 if success or 0 if no more entries in the
+ * current directory
+ */
+static int fat_itr_next(fat_itr *itr)
+{
+ dir_entry *dent;
+
+ itr->name = NULL;
+
+ /*
+ * One logical directory entry consist of following slots:
+ * name[0] Attributes
+ * dent[N - N]: LFN[N - 1] N|0x40 ATTR_VFAT
+ * ...
+ * dent[N - 2]: LFN[1] 2 ATTR_VFAT
+ * dent[N - 1]: LFN[0] 1 ATTR_VFAT
+ * dent[N]: SFN ATTR_ARCH
+ */
+
+ while (1) {
+ dent = next_dent(itr);
+ if (!dent) {
+ itr->dent_start = NULL;
+ return 0;
+ }
+ itr->dent_rem = itr->remaining;
+ itr->dent_start = itr->dent;
+ itr->dent_clust = itr->clust;
+ if (dent->nameext.name[0] == DELETED_FLAG)
+ continue;
+
+ if (dent->attr & ATTR_VOLUME) {
+ if ((dent->attr & ATTR_VFAT) == ATTR_VFAT &&
+ (dent->nameext.name[0] & LAST_LONG_ENTRY_MASK)) {
+ /* long file name */
+ dent = extract_vfat_name(itr);
+ /*
+ * If succeeded, dent has a valid short file
+ * name entry for the current entry.
+ * If failed, itr points to a current bogus
+ * entry. So after fetching a next one,
+ * it may have a short file name entry
+ * for this bogus entry so that we can still
+ * check for a short name.
+ */
+ if (!dent)
+ continue;
+ itr->name = itr->l_name;
+ break;
+ } else {
+ /* Volume label or VFAT entry, skip */
+ continue;
+ }
+ }
+
+ /* short file name */
+ break;
+ }
+
+ get_name(dent, itr->s_name);
+ if (!itr->name)
+ itr->name = itr->s_name;
+
+ return 1;
+}
+
+/**
+ * fat_itr_isdir() - is current cursor position pointing to a directory
+ *
+ * @itr: the iterator
+ * @return true if cursor is at a directory
+ */
+static int fat_itr_isdir(fat_itr *itr)
+{
+ return !!(itr->dent->attr & ATTR_DIR);
+}
+
+/*
+ * Helpers:
+ */
+
+#define TYPE_FILE 0x1
+#define TYPE_DIR 0x2
+#define TYPE_ANY (TYPE_FILE | TYPE_DIR)
+
+/**
+ * fat_itr_resolve() - traverse directory structure to resolve the
+ * requested path.
+ *
+ * Traverse directory structure to the requested path. If the specified
+ * path is to a directory, this will descend into the directory and
+ * leave it iterator at the start of the directory. If the path is to a
+ * file, it will leave the iterator in the parent directory with current
+ * cursor at file's entry in the directory.
+ *
+ * @itr: iterator initialized to root
+ * @path: the requested path
+ * @type: bitmask of allowable file types
+ * @return 0 on success or -errno
+ */
+static int fat_itr_resolve(fat_itr *itr, const char *path, unsigned type)
+{
+ const char *next;
+
+ /* chomp any extra leading slashes: */
+ while (path[0] && ISDIRDELIM(path[0]))
+ path++;
+
+ /* are we at the end? */
+ if (strlen(path) == 0) {
+ if (!(type & TYPE_DIR))
+ return -ENOENT;
+ return 0;
+ }
+
+ /* find length of next path entry: */
+ next = path;
+ while (next[0] && !ISDIRDELIM(next[0]))
+ next++;
+
+ if (itr->is_root) {
+ /* root dir doesn't have "." nor ".." */
+ if ((((next - path) == 1) && !strncmp(path, ".", 1)) ||
+ (((next - path) == 2) && !strncmp(path, "..", 2))) {
+ /* point back to itself */
+ itr->clust = itr->fsdata->root_cluster;
+ itr->next_clust = itr->fsdata->root_cluster;
+ itr->start_clust = itr->fsdata->root_cluster;
+ itr->dent = NULL;
+ itr->remaining = 0;
+ itr->last_cluster = 0;
+
+ if (next[0] == 0) {
+ if (type & TYPE_DIR)
+ return 0;
+ else
+ return -ENOENT;
+ }
+
+ return fat_itr_resolve(itr, next, type);
+ }
+ }
+
+ while (fat_itr_next(itr)) {
+ int match = 0;
+ unsigned n = max(strlen(itr->name), (size_t)(next - path));
+
+ /* check both long and short name: */
+ if (!strncasecmp(path, itr->name, n))
+ match = 1;
+ else if (itr->name != itr->s_name &&
+ !strncasecmp(path, itr->s_name, n))
+ match = 1;
+
+ if (!match)
+ continue;
+
+ if (fat_itr_isdir(itr)) {
+ /* recurse into directory: */
+ fat_itr_child(itr, itr);
+ return fat_itr_resolve(itr, next, type);
+ } else if (next[0]) {
+ /*
+ * If next is not empty then we have a case
+ * like: /path/to/realfile/nonsense
+ */
+ debug("bad trailing path: %s\n", next);
+ return -ENOENT;
+ } else if (!(type & TYPE_FILE)) {
+ return -ENOTDIR;
+ } else {
+ return 0;
+ }
+ }
+
+ return -ENOENT;
+}
+
+int file_fat_detectfs(void)
+{
+ boot_sector bs;
+ volume_info volinfo;
+ int fatsize;
+ char vol_label[12];
+
+ if (cur_dev == NULL) {
+ printf("No current device\n");
+ return 1;
+ }
+
+ if (IS_ENABLED(CONFIG_HAVE_BLOCK_DEVICE)) {
+ printf("Interface: %s\n", blk_get_if_type_name(cur_dev->if_type));
+ printf(" Device %d: ", cur_dev->devnum);
+ dev_print(cur_dev);
+ }
+
+ if (read_bootsectandvi(&bs, &volinfo, &fatsize)) {
+ printf("\nNo valid FAT fs found\n");
+ return 1;
+ }
+
+ memcpy(vol_label, volinfo.volume_label, 11);
+ vol_label[11] = '\0';
+ volinfo.fs_type[5] = '\0';
+
+ printf("Filesystem: %s \"%s\"\n", volinfo.fs_type, vol_label);
+
+ return 0;
+}
+
+int fat_exists(const char *filename)
+{
+ fsdata fsdata;
+ fat_itr *itr;
+ int ret;
+
+ itr = malloc_cache_aligned(sizeof(fat_itr));
+ if (!itr)
+ return 0;
+ ret = fat_itr_root(itr, &fsdata);
+ if (ret)
+ goto out;
+
+ ret = fat_itr_resolve(itr, filename, TYPE_ANY);
+ free(fsdata.fatbuf);
+out:
+ free(itr);
+ return ret == 0;
+}
+
+int fat_size(const char *filename, loff_t *size)
+{
+ fsdata fsdata;
+ fat_itr *itr;
+ int ret;
+
+ itr = malloc_cache_aligned(sizeof(fat_itr));
+ if (!itr)
+ return -ENOMEM;
+ ret = fat_itr_root(itr, &fsdata);
+ if (ret)
+ goto out_free_itr;
+
+ ret = fat_itr_resolve(itr, filename, TYPE_FILE);
+ if (ret) {
+ /*
+ * Directories don't have size, but fs_size() is not
+ * expected to fail if passed a directory path:
+ */
+ free(fsdata.fatbuf);
+ ret = fat_itr_root(itr, &fsdata);
+ if (ret)
+ goto out_free_itr;
+ ret = fat_itr_resolve(itr, filename, TYPE_DIR);
+ if (!ret)
+ *size = 0;
+ goto out_free_both;
+ }
+
+ *size = FAT2CPU32(itr->dent->size);
+out_free_both:
+ free(fsdata.fatbuf);
+out_free_itr:
+ free(itr);
+ return ret;
+}
+
+int file_fat_read_at(const char *filename, loff_t pos, void *buffer,
+ loff_t maxsize, loff_t *actread)
+{
+ fsdata fsdata;
+ fat_itr *itr;
+ int ret;
+
+ itr = malloc_cache_aligned(sizeof(fat_itr));
+ if (!itr)
+ return -ENOMEM;
+ ret = fat_itr_root(itr, &fsdata);
+ if (ret)
+ goto out_free_itr;
+
+ ret = fat_itr_resolve(itr, filename, TYPE_FILE);
+ if (ret)
+ goto out_free_both;
+
+ debug("reading %s at pos %llu\n", filename, pos);
+
+ /* For saving default max clustersize memory allocated to malloc pool */
+ dir_entry *dentptr = itr->dent;
+
+ ret = get_contents(&fsdata, dentptr, pos, buffer, maxsize, actread);
+
+out_free_both:
+ free(fsdata.fatbuf);
+out_free_itr:
+ free(itr);
+ return ret;
+}
+
+int file_fat_read(const char *filename, void *buffer, int maxsize)
+{
+ loff_t actread;
+ int ret;
+
+ ret = file_fat_read_at(filename, 0, buffer, maxsize, &actread);
+ if (ret)
+ return ret;
+ else
+ return actread;
+}
+
+int fat_read_file(const char *filename, void *buf, loff_t offset, loff_t len,
+ loff_t *actread)
+{
+ int ret;
+
+ ret = file_fat_read_at(filename, offset, buf, len, actread);
+ if (ret)
+ printf("** Unable to read file %s **\n", filename);
+
+ return ret;
+}
+
+typedef struct {
+ struct fs_dir_stream parent;
+ struct fs_dirent dirent;
+ fsdata fsdata;
+ fat_itr itr;
+} fat_dir;
+
+int fat_opendir(const char *filename, struct fs_dir_stream **dirsp)
+{
+ fat_dir *dir;
+ int ret;
+
+ dir = malloc_cache_aligned(sizeof(*dir));
+ if (!dir)
+ return -ENOMEM;
+ memset(dir, 0, sizeof(*dir));
+
+ ret = fat_itr_root(&dir->itr, &dir->fsdata);
+ if (ret)
+ goto fail_free_dir;
+
+ ret = fat_itr_resolve(&dir->itr, filename, TYPE_DIR);
+ if (ret)
+ goto fail_free_both;
+
+ *dirsp = (struct fs_dir_stream *)dir;
+ return 0;
+
+fail_free_both:
+ free(dir->fsdata.fatbuf);
+fail_free_dir:
+ free(dir);
+ return ret;
+}
+
+int fat_readdir(struct fs_dir_stream *dirs, struct fs_dirent **dentp)
+{
+ fat_dir *dir = (fat_dir *)dirs;
+ struct fs_dirent *dent = &dir->dirent;
+
+ if (!fat_itr_next(&dir->itr))
+ return -ENOENT;
+
+ memset(dent, 0, sizeof(*dent));
+ strcpy(dent->name, dir->itr.name);
+
+ if (fat_itr_isdir(&dir->itr)) {
+ dent->type = FS_DT_DIR;
+ } else {
+ dent->type = FS_DT_REG;
+ dent->size = FAT2CPU32(dir->itr.dent->size);
+ }
+
+ *dentp = dent;
+
+ return 0;
+}
+
+void fat_closedir(struct fs_dir_stream *dirs)
+{
+ fat_dir *dir = (fat_dir *)dirs;
+ free(dir->fsdata.fatbuf);
+ free(dir);
+}
+
+void fat_close(void)
+{
+}
+
+int fat_uuid(char *uuid_str)
+{
+ boot_sector bs;
+ volume_info volinfo;
+ int fatsize;
+ int ret;
+ u8 *id;
+
+ ret = read_bootsectandvi(&bs, &volinfo, &fatsize);
+ if (ret)
+ return ret;
+
+ id = volinfo.volume_id;
+ sprintf(uuid_str, "%02X%02X-%02X%02X", id[3], id[2], id[1], id[0]);
+
+ return 0;
+}
diff --git a/roms/u-boot/fs/fat/fat_write.c b/roms/u-boot/fs/fat/fat_write.c
new file mode 100644
index 000000000..8ff2f6def
--- /dev/null
+++ b/roms/u-boot/fs/fat/fat_write.c
@@ -0,0 +1,1773 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * fat_write.c
+ *
+ * R/W (V)FAT 12/16/32 filesystem implementation by Donggeun Kim
+ */
+
+#include <common.h>
+#include <command.h>
+#include <config.h>
+#include <div64.h>
+#include <fat.h>
+#include <log.h>
+#include <malloc.h>
+#include <part.h>
+#include <rand.h>
+#include <asm/byteorder.h>
+#include <asm/cache.h>
+#include <linux/ctype.h>
+#include <linux/math64.h>
+#include "fat.c"
+
+static dir_entry *find_directory_entry(fat_itr *itr, char *filename);
+static int new_dir_table(fat_itr *itr);
+
+/* Characters that may only be used in long file names */
+static const char LONG_ONLY_CHARS[] = "+,;=[]";
+
+/* Combined size of the name and ext fields in the directory entry */
+#define SHORT_NAME_SIZE 11
+
+/**
+ * str2fat() - convert string to valid FAT name characters
+ *
+ * Stop when reaching end of @src or a period.
+ * Ignore spaces.
+ * Replace characters that may only be used in long names by underscores.
+ * Convert lower case characters to upper case.
+ *
+ * To avoid assumptions about the code page we do not use characters
+ * above 0x7f for the short name.
+ *
+ * @dest: destination buffer
+ * @src: source buffer
+ * @length: size of destination buffer
+ * Return: number of bytes in destination buffer
+ */
+static int str2fat(char *dest, char *src, int length)
+{
+ int i;
+
+ for (i = 0; i < length; ++src) {
+ char c = *src;
+
+ if (!c || c == '.')
+ break;
+ if (c == ' ')
+ continue;
+ if (strchr(LONG_ONLY_CHARS, c) || c > 0x7f)
+ c = '_';
+ else if (c >= 'a' && c <= 'z')
+ c &= 0xdf;
+ dest[i] = c;
+ ++i;
+ }
+ return i;
+}
+
+/**
+ * fat_move_to_cluster() - position to first directory entry in cluster
+ *
+ * @itr: directory iterator
+ * @cluster cluster
+ * Return: 0 for success, -EIO on error
+ */
+static int fat_move_to_cluster(fat_itr *itr, unsigned int cluster)
+{
+ unsigned int nbytes;
+
+ /* position to the start of the directory */
+ itr->next_clust = cluster;
+ itr->last_cluster = 0;
+ if (!fat_next_cluster(itr, &nbytes))
+ return -EIO;
+ itr->dent = (dir_entry *)itr->block;
+ itr->remaining = nbytes / sizeof(dir_entry) - 1;
+ return 0;
+}
+
+/**
+ * set_name() - set short name in directory entry
+ *
+ * The function determines if the @filename is a valid short name.
+ * In this case no long name is needed.
+ *
+ * If a long name is needed, a short name is constructed.
+ *
+ * @itr: directory iterator
+ * @filename: long file name
+ * @shortname: buffer of 11 bytes to receive chosen short name and extension
+ * Return: number of directory entries needed, negative on error
+ */
+static int set_name(fat_itr *itr, const char *filename, char *shortname)
+{
+ char *period;
+ char *pos;
+ int period_location;
+ char buf[13];
+ int i;
+ int ret;
+ struct nameext dirent;
+
+ if (!filename)
+ return -EIO;
+
+ /* Initialize buffer */
+ memset(&dirent, ' ', sizeof(dirent));
+
+ /* Convert filename to upper case short name */
+ period = strrchr(filename, '.');
+ pos = (char *)filename;
+ if (*pos == '.') {
+ pos = period + 1;
+ period = 0;
+ }
+ if (period)
+ str2fat(dirent.ext, period + 1, sizeof(dirent.ext));
+ period_location = str2fat(dirent.name, pos, sizeof(dirent.name));
+ if (period_location < 0)
+ return period_location;
+ if (*dirent.name == ' ')
+ *dirent.name = '_';
+ /* 0xe5 signals a deleted directory entry. Replace it by 0x05. */
+ if (*dirent.name == 0xe5)
+ *dirent.name = 0x05;
+
+ /* If filename and short name are the same, quit. */
+ sprintf(buf, "%.*s.%.3s", period_location, dirent.name, dirent.ext);
+ if (!strcmp(buf, filename)) {
+ ret = 1;
+ goto out;
+ }
+
+ /* Construct an indexed short name */
+ for (i = 1; i < 0x200000; ++i) {
+ int suffix_len;
+ int suffix_start;
+ int j;
+
+ /* To speed up the search use random numbers */
+ if (i < 10) {
+ j = i;
+ } else {
+ j = 30 - fls(i);
+ j = 10 + (rand() >> j);
+ }
+ sprintf(buf, "~%d", j);
+ suffix_len = strlen(buf);
+ suffix_start = 8 - suffix_len;
+ if (suffix_start > period_location)
+ suffix_start = period_location;
+ memcpy(dirent.name + suffix_start, buf, suffix_len);
+ if (*dirent.ext != ' ')
+ sprintf(buf, "%.*s.%.3s", suffix_start + suffix_len,
+ dirent.name, dirent.ext);
+ else
+ sprintf(buf, "%.*s", suffix_start + suffix_len,
+ dirent.name);
+ debug("generated short name: %s\n", buf);
+
+ /* Check that the short name does not exist yet. */
+ ret = fat_move_to_cluster(itr, itr->start_clust);
+ if (ret)
+ return ret;
+ if (find_directory_entry(itr, buf))
+ continue;
+
+ debug("chosen short name: %s\n", buf);
+ /* Each long name directory entry takes 13 characters. */
+ ret = (strlen(filename) + 25) / 13;
+ goto out;
+ }
+ return -EIO;
+out:
+ memcpy(shortname, &dirent, SHORT_NAME_SIZE);
+ return ret;
+}
+
+static int total_sector;
+static int disk_write(__u32 block, __u32 nr_blocks, void *buf)
+{
+ ulong ret;
+
+ if (!cur_dev)
+ return -1;
+
+ if (cur_part_info.start + block + nr_blocks >
+ cur_part_info.start + total_sector) {
+ printf("error: overflow occurs\n");
+ return -1;
+ }
+
+ ret = blk_dwrite(cur_dev, cur_part_info.start + block, nr_blocks, buf);
+ if (nr_blocks && ret == 0)
+ return -1;
+
+ return ret;
+}
+
+/*
+ * Write fat buffer into block device
+ */
+static int flush_dirty_fat_buffer(fsdata *mydata)
+{
+ int getsize = FATBUFBLOCKS;
+ __u32 fatlength = mydata->fatlength;
+ __u8 *bufptr = mydata->fatbuf;
+ __u32 startblock = mydata->fatbufnum * FATBUFBLOCKS;
+
+ debug("debug: evicting %d, dirty: %d\n", mydata->fatbufnum,
+ (int)mydata->fat_dirty);
+
+ if ((!mydata->fat_dirty) || (mydata->fatbufnum == -1))
+ return 0;
+
+ /* Cap length if fatlength is not a multiple of FATBUFBLOCKS */
+ if (startblock + getsize > fatlength)
+ getsize = fatlength - startblock;
+
+ startblock += mydata->fat_sect;
+
+ /* Write FAT buf */
+ if (disk_write(startblock, getsize, bufptr) < 0) {
+ debug("error: writing FAT blocks\n");
+ return -1;
+ }
+
+ if (mydata->fats == 2) {
+ /* Update corresponding second FAT blocks */
+ startblock += mydata->fatlength;
+ if (disk_write(startblock, getsize, bufptr) < 0) {
+ debug("error: writing second FAT blocks\n");
+ return -1;
+ }
+ }
+ mydata->fat_dirty = 0;
+
+ return 0;
+}
+
+/**
+ * fat_find_empty_dentries() - find a sequence of available directory entries
+ *
+ * @itr: directory iterator
+ * @count: number of directory entries to find
+ * Return: 0 on success or negative error number
+ */
+static int fat_find_empty_dentries(fat_itr *itr, int count)
+{
+ unsigned int cluster;
+ dir_entry *dent;
+ int remaining;
+ unsigned int n = 0;
+ int ret;
+
+ ret = fat_move_to_cluster(itr, itr->start_clust);
+ if (ret)
+ return ret;
+
+ for (;;) {
+ if (!itr->dent) {
+ log_debug("Not enough directory entries available\n");
+ return -ENOSPC;
+ }
+ switch (itr->dent->nameext.name[0]) {
+ case 0x00:
+ case DELETED_FLAG:
+ if (!n) {
+ /* Remember first deleted directory entry */
+ cluster = itr->clust;
+ dent = itr->dent;
+ remaining = itr->remaining;
+ }
+ ++n;
+ if (n == count)
+ goto out;
+ break;
+ default:
+ n = 0;
+ break;
+ }
+
+ next_dent(itr);
+ if (!itr->dent &&
+ (!itr->is_root || itr->fsdata->fatsize == 32) &&
+ new_dir_table(itr))
+ return -ENOSPC;
+ }
+out:
+ /* Position back to first directory entry */
+ if (itr->clust != cluster) {
+ ret = fat_move_to_cluster(itr, cluster);
+ if (ret)
+ return ret;
+ }
+ itr->dent = dent;
+ itr->remaining = remaining;
+ return 0;
+}
+
+/*
+ * Set the file name information from 'name' into 'slotptr',
+ */
+static int str2slot(dir_slot *slotptr, const char *name, int *idx)
+{
+ int j, end_idx = 0;
+
+ for (j = 0; j <= 8; j += 2) {
+ if (name[*idx] == 0x00) {
+ slotptr->name0_4[j] = 0;
+ slotptr->name0_4[j + 1] = 0;
+ end_idx++;
+ goto name0_4;
+ }
+ slotptr->name0_4[j] = name[*idx];
+ (*idx)++;
+ end_idx++;
+ }
+ for (j = 0; j <= 10; j += 2) {
+ if (name[*idx] == 0x00) {
+ slotptr->name5_10[j] = 0;
+ slotptr->name5_10[j + 1] = 0;
+ end_idx++;
+ goto name5_10;
+ }
+ slotptr->name5_10[j] = name[*idx];
+ (*idx)++;
+ end_idx++;
+ }
+ for (j = 0; j <= 2; j += 2) {
+ if (name[*idx] == 0x00) {
+ slotptr->name11_12[j] = 0;
+ slotptr->name11_12[j + 1] = 0;
+ end_idx++;
+ goto name11_12;
+ }
+ slotptr->name11_12[j] = name[*idx];
+ (*idx)++;
+ end_idx++;
+ }
+
+ if (name[*idx] == 0x00)
+ return 1;
+
+ return 0;
+/* Not used characters are filled with 0xff 0xff */
+name0_4:
+ for (; end_idx < 5; end_idx++) {
+ slotptr->name0_4[end_idx * 2] = 0xff;
+ slotptr->name0_4[end_idx * 2 + 1] = 0xff;
+ }
+ end_idx = 5;
+name5_10:
+ end_idx -= 5;
+ for (; end_idx < 6; end_idx++) {
+ slotptr->name5_10[end_idx * 2] = 0xff;
+ slotptr->name5_10[end_idx * 2 + 1] = 0xff;
+ }
+ end_idx = 11;
+name11_12:
+ end_idx -= 11;
+ for (; end_idx < 2; end_idx++) {
+ slotptr->name11_12[end_idx * 2] = 0xff;
+ slotptr->name11_12[end_idx * 2 + 1] = 0xff;
+ }
+
+ return 1;
+}
+
+static int flush_dir(fat_itr *itr);
+
+/**
+ * fill_dir_slot() - fill directory entries for long name
+ *
+ * @itr: directory iterator
+ * @l_name: long name
+ * @shortname: short name
+ * Return: 0 for success, -errno otherwise
+ */
+static int
+fill_dir_slot(fat_itr *itr, const char *l_name, const char *shortname)
+{
+ __u8 temp_dir_slot_buffer[MAX_LFN_SLOT * sizeof(dir_slot)];
+ dir_slot *slotptr = (dir_slot *)temp_dir_slot_buffer;
+ __u8 counter = 0, checksum;
+ int idx = 0, ret;
+
+ /* Get short file name checksum value */
+ checksum = mkcksum((void *)shortname);
+
+ do {
+ memset(slotptr, 0x00, sizeof(dir_slot));
+ ret = str2slot(slotptr, l_name, &idx);
+ slotptr->id = ++counter;
+ slotptr->attr = ATTR_VFAT;
+ slotptr->alias_checksum = checksum;
+ slotptr++;
+ } while (ret == 0);
+
+ slotptr--;
+ slotptr->id |= LAST_LONG_ENTRY_MASK;
+
+ while (counter >= 1) {
+ memcpy(itr->dent, slotptr, sizeof(dir_slot));
+ slotptr--;
+ counter--;
+
+ if (!itr->remaining) {
+ /* Write directory table to device */
+ ret = flush_dir(itr);
+ if (ret)
+ return ret;
+ }
+
+ next_dent(itr);
+ if (!itr->dent)
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/*
+ * Set the entry at index 'entry' in a FAT (12/16/32) table.
+ */
+static int set_fatent_value(fsdata *mydata, __u32 entry, __u32 entry_value)
+{
+ __u32 bufnum, offset, off16;
+ __u16 val1, val2;
+
+ switch (mydata->fatsize) {
+ case 32:
+ bufnum = entry / FAT32BUFSIZE;
+ offset = entry - bufnum * FAT32BUFSIZE;
+ break;
+ case 16:
+ bufnum = entry / FAT16BUFSIZE;
+ offset = entry - bufnum * FAT16BUFSIZE;
+ break;
+ case 12:
+ bufnum = entry / FAT12BUFSIZE;
+ offset = entry - bufnum * FAT12BUFSIZE;
+ break;
+ default:
+ /* Unsupported FAT size */
+ return -1;
+ }
+
+ /* Read a new block of FAT entries into the cache. */
+ if (bufnum != mydata->fatbufnum) {
+ int getsize = FATBUFBLOCKS;
+ __u8 *bufptr = mydata->fatbuf;
+ __u32 fatlength = mydata->fatlength;
+ __u32 startblock = bufnum * FATBUFBLOCKS;
+
+ /* Cap length if fatlength is not a multiple of FATBUFBLOCKS */
+ if (startblock + getsize > fatlength)
+ getsize = fatlength - startblock;
+
+ if (flush_dirty_fat_buffer(mydata) < 0)
+ return -1;
+
+ startblock += mydata->fat_sect;
+
+ if (disk_read(startblock, getsize, bufptr) < 0) {
+ debug("Error reading FAT blocks\n");
+ return -1;
+ }
+ mydata->fatbufnum = bufnum;
+ }
+
+ /* Mark as dirty */
+ mydata->fat_dirty = 1;
+
+ /* Set the actual entry */
+ switch (mydata->fatsize) {
+ case 32:
+ ((__u32 *) mydata->fatbuf)[offset] = cpu_to_le32(entry_value);
+ break;
+ case 16:
+ ((__u16 *) mydata->fatbuf)[offset] = cpu_to_le16(entry_value);
+ break;
+ case 12:
+ off16 = (offset * 3) / 4;
+
+ switch (offset & 0x3) {
+ case 0:
+ val1 = cpu_to_le16(entry_value) & 0xfff;
+ ((__u16 *)mydata->fatbuf)[off16] &= ~0xfff;
+ ((__u16 *)mydata->fatbuf)[off16] |= val1;
+ break;
+ case 1:
+ val1 = cpu_to_le16(entry_value) & 0xf;
+ val2 = (cpu_to_le16(entry_value) >> 4) & 0xff;
+
+ ((__u16 *)mydata->fatbuf)[off16] &= ~0xf000;
+ ((__u16 *)mydata->fatbuf)[off16] |= (val1 << 12);
+
+ ((__u16 *)mydata->fatbuf)[off16 + 1] &= ~0xff;
+ ((__u16 *)mydata->fatbuf)[off16 + 1] |= val2;
+ break;
+ case 2:
+ val1 = cpu_to_le16(entry_value) & 0xff;
+ val2 = (cpu_to_le16(entry_value) >> 8) & 0xf;
+
+ ((__u16 *)mydata->fatbuf)[off16] &= ~0xff00;
+ ((__u16 *)mydata->fatbuf)[off16] |= (val1 << 8);
+
+ ((__u16 *)mydata->fatbuf)[off16 + 1] &= ~0xf;
+ ((__u16 *)mydata->fatbuf)[off16 + 1] |= val2;
+ break;
+ case 3:
+ val1 = cpu_to_le16(entry_value) & 0xfff;
+ ((__u16 *)mydata->fatbuf)[off16] &= ~0xfff0;
+ ((__u16 *)mydata->fatbuf)[off16] |= (val1 << 4);
+ break;
+ default:
+ break;
+ }
+
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Determine the next free cluster after 'entry' in a FAT (12/16/32) table
+ * and link it to 'entry'. EOC marker is not set on returned entry.
+ */
+static __u32 determine_fatent(fsdata *mydata, __u32 entry)
+{
+ __u32 next_fat, next_entry = entry + 1;
+
+ while (1) {
+ next_fat = get_fatent(mydata, next_entry);
+ if (next_fat == 0) {
+ /* found free entry, link to entry */
+ set_fatent_value(mydata, entry, next_entry);
+ break;
+ }
+ next_entry++;
+ }
+ debug("FAT%d: entry: %08x, entry_value: %04x\n",
+ mydata->fatsize, entry, next_entry);
+
+ return next_entry;
+}
+
+/**
+ * set_sectors() - write data to sectors
+ *
+ * Write 'size' bytes from 'buffer' into the specified sector.
+ *
+ * @mydata: data to be written
+ * @startsect: sector to be written to
+ * @buffer: data to be written
+ * @size: bytes to be written (but not more than the size of a cluster)
+ * Return: 0 on success, -1 otherwise
+ */
+static int
+set_sectors(fsdata *mydata, u32 startsect, u8 *buffer, u32 size)
+{
+ int ret;
+
+ debug("startsect: %d\n", startsect);
+
+ if ((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1)) {
+ ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
+
+ debug("FAT: Misaligned buffer address (%p)\n", buffer);
+
+ while (size >= mydata->sect_size) {
+ memcpy(tmpbuf, buffer, mydata->sect_size);
+ ret = disk_write(startsect++, 1, tmpbuf);
+ if (ret != 1) {
+ debug("Error writing data (got %d)\n", ret);
+ return -1;
+ }
+
+ buffer += mydata->sect_size;
+ size -= mydata->sect_size;
+ }
+ } else if (size >= mydata->sect_size) {
+ u32 nsects;
+
+ nsects = size / mydata->sect_size;
+ ret = disk_write(startsect, nsects, buffer);
+ if (ret != nsects) {
+ debug("Error writing data (got %d)\n", ret);
+ return -1;
+ }
+
+ startsect += nsects;
+ buffer += nsects * mydata->sect_size;
+ size -= nsects * mydata->sect_size;
+ }
+
+ if (size) {
+ ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
+ /* Do not leak content of stack */
+ memset(tmpbuf, 0, mydata->sect_size);
+ memcpy(tmpbuf, buffer, size);
+ ret = disk_write(startsect, 1, tmpbuf);
+ if (ret != 1) {
+ debug("Error writing data (got %d)\n", ret);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * set_cluster() - write data to cluster
+ *
+ * Write 'size' bytes from 'buffer' into the specified cluster.
+ *
+ * @mydata: data to be written
+ * @clustnum: cluster to be written to
+ * @buffer: data to be written
+ * @size: bytes to be written (but not more than the size of a cluster)
+ * Return: 0 on success, -1 otherwise
+ */
+static int
+set_cluster(fsdata *mydata, u32 clustnum, u8 *buffer, u32 size)
+{
+ return set_sectors(mydata, clust_to_sect(mydata, clustnum),
+ buffer, size);
+}
+
+/**
+ * flush_dir() - flush directory
+ *
+ * @itr: directory iterator
+ * Return: 0 for success, -EIO on error
+ */
+static int flush_dir(fat_itr *itr)
+{
+ fsdata *mydata = itr->fsdata;
+ u32 startsect, sect_offset, nsects;
+ int ret;
+
+ if (!itr->is_root || mydata->fatsize == 32) {
+ ret = set_cluster(mydata, itr->clust, itr->block,
+ mydata->clust_size * mydata->sect_size);
+ goto out;
+ }
+
+ sect_offset = itr->clust * mydata->clust_size;
+ startsect = mydata->rootdir_sect + sect_offset;
+ /* do not write past the end of rootdir */
+ nsects = min_t(u32, mydata->clust_size,
+ mydata->rootdir_size - sect_offset);
+
+ ret = set_sectors(mydata, startsect, itr->block,
+ nsects * mydata->sect_size);
+out:
+ if (ret) {
+ log_err("Error: writing directory entry\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+/*
+ * Read and modify data on existing and consecutive cluster blocks
+ */
+static int
+get_set_cluster(fsdata *mydata, __u32 clustnum, loff_t pos, __u8 *buffer,
+ loff_t size, loff_t *gotsize)
+{
+ static u8 *tmpbuf_cluster;
+ unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
+ __u32 startsect;
+ loff_t wsize;
+ int clustcount, i, ret;
+
+ *gotsize = 0;
+ if (!size)
+ return 0;
+
+ if (!tmpbuf_cluster) {
+ tmpbuf_cluster = memalign(ARCH_DMA_MINALIGN, MAX_CLUSTSIZE);
+ if (!tmpbuf_cluster)
+ return -1;
+ }
+
+ assert(pos < bytesperclust);
+ startsect = clust_to_sect(mydata, clustnum);
+
+ debug("clustnum: %d, startsect: %d, pos: %lld\n",
+ clustnum, startsect, pos);
+
+ /* partial write at beginning */
+ if (pos) {
+ wsize = min(bytesperclust - pos, size);
+ ret = disk_read(startsect, mydata->clust_size, tmpbuf_cluster);
+ if (ret != mydata->clust_size) {
+ debug("Error reading data (got %d)\n", ret);
+ return -1;
+ }
+
+ memcpy(tmpbuf_cluster + pos, buffer, wsize);
+ ret = disk_write(startsect, mydata->clust_size, tmpbuf_cluster);
+ if (ret != mydata->clust_size) {
+ debug("Error writing data (got %d)\n", ret);
+ return -1;
+ }
+
+ size -= wsize;
+ buffer += wsize;
+ *gotsize += wsize;
+
+ startsect += mydata->clust_size;
+
+ if (!size)
+ return 0;
+ }
+
+ /* full-cluster write */
+ if (size >= bytesperclust) {
+ clustcount = lldiv(size, bytesperclust);
+
+ if (!((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1))) {
+ wsize = clustcount * bytesperclust;
+ ret = disk_write(startsect,
+ clustcount * mydata->clust_size,
+ buffer);
+ if (ret != clustcount * mydata->clust_size) {
+ debug("Error writing data (got %d)\n", ret);
+ return -1;
+ }
+
+ size -= wsize;
+ buffer += wsize;
+ *gotsize += wsize;
+
+ startsect += clustcount * mydata->clust_size;
+ } else {
+ for (i = 0; i < clustcount; i++) {
+ memcpy(tmpbuf_cluster, buffer, bytesperclust);
+ ret = disk_write(startsect,
+ mydata->clust_size,
+ tmpbuf_cluster);
+ if (ret != mydata->clust_size) {
+ debug("Error writing data (got %d)\n",
+ ret);
+ return -1;
+ }
+
+ size -= bytesperclust;
+ buffer += bytesperclust;
+ *gotsize += bytesperclust;
+
+ startsect += mydata->clust_size;
+ }
+ }
+ }
+
+ /* partial write at end */
+ if (size) {
+ wsize = size;
+ ret = disk_read(startsect, mydata->clust_size, tmpbuf_cluster);
+ if (ret != mydata->clust_size) {
+ debug("Error reading data (got %d)\n", ret);
+ return -1;
+ }
+ memcpy(tmpbuf_cluster, buffer, wsize);
+ ret = disk_write(startsect, mydata->clust_size, tmpbuf_cluster);
+ if (ret != mydata->clust_size) {
+ debug("Error writing data (got %d)\n", ret);
+ return -1;
+ }
+
+ size -= wsize;
+ *gotsize += wsize;
+ }
+
+ assert(!size);
+
+ return 0;
+}
+
+/*
+ * Find the first empty cluster
+ */
+static int find_empty_cluster(fsdata *mydata)
+{
+ __u32 fat_val, entry = 3;
+
+ while (1) {
+ fat_val = get_fatent(mydata, entry);
+ if (fat_val == 0)
+ break;
+ entry++;
+ }
+
+ return entry;
+}
+
+/**
+ * new_dir_table() - allocate a cluster for additional directory entries
+ *
+ * @itr: directory iterator
+ * Return: 0 on success, -EIO otherwise
+ */
+static int new_dir_table(fat_itr *itr)
+{
+ fsdata *mydata = itr->fsdata;
+ int dir_newclust = 0;
+ int dir_oldclust = itr->clust;
+ unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
+
+ dir_newclust = find_empty_cluster(mydata);
+
+ /*
+ * Flush before updating FAT to ensure valid directory structure
+ * in case of failure.
+ */
+ itr->clust = dir_newclust;
+ itr->next_clust = dir_newclust;
+ memset(itr->block, 0x00, bytesperclust);
+ if (flush_dir(itr))
+ return -EIO;
+
+ set_fatent_value(mydata, dir_oldclust, dir_newclust);
+ if (mydata->fatsize == 32)
+ set_fatent_value(mydata, dir_newclust, 0xffffff8);
+ else if (mydata->fatsize == 16)
+ set_fatent_value(mydata, dir_newclust, 0xfff8);
+ else if (mydata->fatsize == 12)
+ set_fatent_value(mydata, dir_newclust, 0xff8);
+
+ if (flush_dirty_fat_buffer(mydata) < 0)
+ return -EIO;
+
+ itr->dent = (dir_entry *)itr->block;
+ itr->last_cluster = 1;
+ itr->remaining = bytesperclust / sizeof(dir_entry) - 1;
+
+ return 0;
+}
+
+/*
+ * Set empty cluster from 'entry' to the end of a file
+ */
+static int clear_fatent(fsdata *mydata, __u32 entry)
+{
+ __u32 fat_val;
+
+ while (!CHECK_CLUST(entry, mydata->fatsize)) {
+ fat_val = get_fatent(mydata, entry);
+ if (fat_val != 0)
+ set_fatent_value(mydata, entry, 0);
+ else
+ break;
+
+ entry = fat_val;
+ }
+
+ /* Flush fat buffer */
+ if (flush_dirty_fat_buffer(mydata) < 0)
+ return -1;
+
+ return 0;
+}
+
+/*
+ * Set start cluster in directory entry
+ */
+static void set_start_cluster(const fsdata *mydata, dir_entry *dentptr,
+ __u32 start_cluster)
+{
+ if (mydata->fatsize == 32)
+ dentptr->starthi =
+ cpu_to_le16((start_cluster & 0xffff0000) >> 16);
+ dentptr->start = cpu_to_le16(start_cluster & 0xffff);
+}
+
+/*
+ * Check whether adding a file makes the file system to
+ * exceed the size of the block device
+ * Return -1 when overflow occurs, otherwise return 0
+ */
+static int check_overflow(fsdata *mydata, __u32 clustnum, loff_t size)
+{
+ __u32 startsect, sect_num, offset;
+
+ if (clustnum > 0)
+ startsect = clust_to_sect(mydata, clustnum);
+ else
+ startsect = mydata->rootdir_sect;
+
+ sect_num = div_u64_rem(size, mydata->sect_size, &offset);
+
+ if (offset != 0)
+ sect_num++;
+
+ if (startsect + sect_num > total_sector)
+ return -1;
+ return 0;
+}
+
+/*
+ * Write at most 'maxsize' bytes from 'buffer' into
+ * the file associated with 'dentptr'
+ * Update the number of bytes written in *gotsize and return 0
+ * or return -1 on fatal errors.
+ */
+static int
+set_contents(fsdata *mydata, dir_entry *dentptr, loff_t pos, __u8 *buffer,
+ loff_t maxsize, loff_t *gotsize)
+{
+ unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
+ __u32 curclust = START(dentptr);
+ __u32 endclust = 0, newclust = 0;
+ u64 cur_pos, filesize;
+ loff_t offset, actsize, wsize;
+
+ *gotsize = 0;
+ filesize = pos + maxsize;
+
+ debug("%llu bytes\n", filesize);
+
+ if (!filesize) {
+ if (!curclust)
+ return 0;
+ if (!CHECK_CLUST(curclust, mydata->fatsize) ||
+ IS_LAST_CLUST(curclust, mydata->fatsize)) {
+ clear_fatent(mydata, curclust);
+ set_start_cluster(mydata, dentptr, 0);
+ return 0;
+ }
+ debug("curclust: 0x%x\n", curclust);
+ debug("Invalid FAT entry\n");
+ return -1;
+ }
+
+ if (!curclust) {
+ assert(pos == 0);
+ goto set_clusters;
+ }
+
+ /* go to cluster at pos */
+ cur_pos = bytesperclust;
+ while (1) {
+ if (pos <= cur_pos)
+ break;
+ if (IS_LAST_CLUST(curclust, mydata->fatsize))
+ break;
+
+ newclust = get_fatent(mydata, curclust);
+ if (!IS_LAST_CLUST(newclust, mydata->fatsize) &&
+ CHECK_CLUST(newclust, mydata->fatsize)) {
+ debug("curclust: 0x%x\n", curclust);
+ debug("Invalid FAT entry\n");
+ return -1;
+ }
+
+ cur_pos += bytesperclust;
+ curclust = newclust;
+ }
+ if (IS_LAST_CLUST(curclust, mydata->fatsize)) {
+ assert(pos == cur_pos);
+ goto set_clusters;
+ }
+
+ assert(pos < cur_pos);
+ cur_pos -= bytesperclust;
+
+ /* overwrite */
+ assert(IS_LAST_CLUST(curclust, mydata->fatsize) ||
+ !CHECK_CLUST(curclust, mydata->fatsize));
+
+ while (1) {
+ /* search for allocated consecutive clusters */
+ actsize = bytesperclust;
+ endclust = curclust;
+ while (1) {
+ if (filesize <= (cur_pos + actsize))
+ break;
+
+ newclust = get_fatent(mydata, endclust);
+
+ if (newclust != endclust + 1)
+ break;
+ if (IS_LAST_CLUST(newclust, mydata->fatsize))
+ break;
+ if (CHECK_CLUST(newclust, mydata->fatsize)) {
+ debug("curclust: 0x%x\n", curclust);
+ debug("Invalid FAT entry\n");
+ return -1;
+ }
+
+ actsize += bytesperclust;
+ endclust = newclust;
+ }
+
+ /* overwrite to <curclust..endclust> */
+ if (pos < cur_pos)
+ offset = 0;
+ else
+ offset = pos - cur_pos;
+ wsize = min_t(unsigned long long, actsize, filesize - cur_pos);
+ wsize -= offset;
+
+ if (get_set_cluster(mydata, curclust, offset,
+ buffer, wsize, &actsize)) {
+ printf("Error get-and-setting cluster\n");
+ return -1;
+ }
+ buffer += wsize;
+ *gotsize += wsize;
+ cur_pos += offset + wsize;
+
+ if (filesize <= cur_pos)
+ break;
+
+ if (IS_LAST_CLUST(newclust, mydata->fatsize))
+ /* no more clusters */
+ break;
+
+ curclust = newclust;
+ }
+
+ if (filesize <= cur_pos) {
+ /* no more write */
+ newclust = get_fatent(mydata, endclust);
+ if (!IS_LAST_CLUST(newclust, mydata->fatsize)) {
+ /* truncate the rest */
+ clear_fatent(mydata, newclust);
+
+ /* Mark end of file in FAT */
+ if (mydata->fatsize == 12)
+ newclust = 0xfff;
+ else if (mydata->fatsize == 16)
+ newclust = 0xffff;
+ else if (mydata->fatsize == 32)
+ newclust = 0xfffffff;
+ set_fatent_value(mydata, endclust, newclust);
+ }
+
+ return 0;
+ }
+
+ curclust = endclust;
+ filesize -= cur_pos;
+ assert(!do_div(cur_pos, bytesperclust));
+
+set_clusters:
+ /* allocate and write */
+ assert(!pos);
+
+ /* Assure that curclust is valid */
+ if (!curclust) {
+ curclust = find_empty_cluster(mydata);
+ set_start_cluster(mydata, dentptr, curclust);
+ } else {
+ newclust = get_fatent(mydata, curclust);
+
+ if (IS_LAST_CLUST(newclust, mydata->fatsize)) {
+ newclust = determine_fatent(mydata, curclust);
+ set_fatent_value(mydata, curclust, newclust);
+ curclust = newclust;
+ } else {
+ debug("error: something wrong\n");
+ return -1;
+ }
+ }
+
+ /* TODO: already partially written */
+ if (check_overflow(mydata, curclust, filesize)) {
+ printf("Error: no space left: %llu\n", filesize);
+ return -1;
+ }
+
+ actsize = bytesperclust;
+ endclust = curclust;
+ do {
+ /* search for consecutive clusters */
+ while (actsize < filesize) {
+ newclust = determine_fatent(mydata, endclust);
+
+ if ((newclust - 1) != endclust)
+ /* write to <curclust..endclust> */
+ goto getit;
+
+ if (CHECK_CLUST(newclust, mydata->fatsize)) {
+ debug("newclust: 0x%x\n", newclust);
+ debug("Invalid FAT entry\n");
+ return 0;
+ }
+ endclust = newclust;
+ actsize += bytesperclust;
+ }
+
+ /* set remaining bytes */
+ actsize = filesize;
+ if (set_cluster(mydata, curclust, buffer, (u32)actsize) != 0) {
+ debug("error: writing cluster\n");
+ return -1;
+ }
+ *gotsize += actsize;
+
+ /* Mark end of file in FAT */
+ if (mydata->fatsize == 12)
+ newclust = 0xfff;
+ else if (mydata->fatsize == 16)
+ newclust = 0xffff;
+ else if (mydata->fatsize == 32)
+ newclust = 0xfffffff;
+ set_fatent_value(mydata, endclust, newclust);
+
+ return 0;
+getit:
+ if (set_cluster(mydata, curclust, buffer, (u32)actsize) != 0) {
+ debug("error: writing cluster\n");
+ return -1;
+ }
+ *gotsize += actsize;
+ filesize -= actsize;
+ buffer += actsize;
+
+ if (CHECK_CLUST(newclust, mydata->fatsize)) {
+ debug("newclust: 0x%x\n", newclust);
+ debug("Invalid FAT entry\n");
+ return 0;
+ }
+ actsize = bytesperclust;
+ curclust = endclust = newclust;
+ } while (1);
+
+ return 0;
+}
+
+/**
+ * fill_dentry() - fill directory entry with shortname
+ *
+ * @mydata: private filesystem parameters
+ * @dentptr: directory entry
+ * @shortname: chosen short name
+ * @start_cluster: first cluster of file
+ * @size: file size
+ * @attr: file attributes
+ */
+static void fill_dentry(fsdata *mydata, dir_entry *dentptr,
+ const char *shortname, __u32 start_cluster, __u32 size, __u8 attr)
+{
+ memset(dentptr, 0, sizeof(*dentptr));
+
+ set_start_cluster(mydata, dentptr, start_cluster);
+ dentptr->size = cpu_to_le32(size);
+
+ dentptr->attr = attr;
+
+ memcpy(&dentptr->nameext, shortname, SHORT_NAME_SIZE);
+}
+
+/**
+ * find_directory_entry() - find a directory entry by filename
+ *
+ * @itr: directory iterator
+ * @filename: name of file to find
+ * Return: directory entry or NULL
+ */
+static dir_entry *find_directory_entry(fat_itr *itr, char *filename)
+{
+ int match = 0;
+
+ while (fat_itr_next(itr)) {
+ /* check both long and short name: */
+ if (!strcasecmp(filename, itr->name))
+ match = 1;
+ else if (itr->name != itr->s_name &&
+ !strcasecmp(filename, itr->s_name))
+ match = 1;
+
+ if (!match)
+ continue;
+
+ if (itr->dent->nameext.name[0] == '\0')
+ return NULL;
+ else
+ return itr->dent;
+ }
+
+ return NULL;
+}
+
+static int split_filename(char *filename, char **dirname, char **basename)
+{
+ char *p, *last_slash, *last_slash_cont;
+
+again:
+ p = filename;
+ last_slash = NULL;
+ last_slash_cont = NULL;
+ while (*p) {
+ if (ISDIRDELIM(*p)) {
+ last_slash = p;
+ last_slash_cont = p;
+ /* continuous slashes */
+ while (ISDIRDELIM(*p))
+ last_slash_cont = p++;
+ if (!*p)
+ break;
+ }
+ p++;
+ }
+
+ if (last_slash) {
+ if (last_slash_cont == (filename + strlen(filename) - 1)) {
+ /* remove trailing slashes */
+ *last_slash = '\0';
+ goto again;
+ }
+
+ if (last_slash == filename) {
+ /* avoid ""(null) directory */
+ *dirname = "/";
+ } else {
+ *last_slash = '\0';
+ *dirname = filename;
+ }
+
+ *last_slash_cont = '\0';
+ filename = last_slash_cont + 1;
+ } else {
+ *dirname = "/"; /* root by default */
+ }
+
+ /*
+ * The FAT32 File System Specification v1.03 requires leading and
+ * trailing spaces as well as trailing periods to be ignored.
+ */
+ for (; *filename == ' '; ++filename)
+ ;
+
+ /* Keep special entries '.' and '..' */
+ if (filename[0] == '.' &&
+ (!filename[1] || (filename[1] == '.' && !filename[2])))
+ goto done;
+
+ /* Remove trailing periods and spaces */
+ for (p = filename + strlen(filename) - 1; p >= filename; --p) {
+ switch (*p) {
+ case ' ':
+ case '.':
+ *p = 0;
+ break;
+ default:
+ goto done;
+ }
+ }
+
+done:
+ *basename = filename;
+
+ return 0;
+}
+
+/**
+ * normalize_longname() - check long file name and convert to lower case
+ *
+ * We assume here that the FAT file system is using an 8bit code page.
+ * Linux typically uses CP437, EDK2 assumes CP1250.
+ *
+ * @l_filename: preallocated buffer receiving the normalized name
+ * @filename: filename to normalize
+ * Return: 0 on success, -1 on failure
+ */
+static int normalize_longname(char *l_filename, const char *filename)
+{
+ const char *p, illegal[] = "<>:\"/\\|?*";
+ size_t len;
+
+ len = strlen(filename);
+ if (!len || len >= VFAT_MAXLEN_BYTES || filename[len - 1] == '.')
+ return -1;
+
+ for (p = filename; *p; ++p) {
+ if ((unsigned char)*p < 0x20)
+ return -1;
+ if (strchr(illegal, *p))
+ return -1;
+ }
+
+ strcpy(l_filename, filename);
+ downcase(l_filename, VFAT_MAXLEN_BYTES);
+
+ return 0;
+}
+
+int file_fat_write_at(const char *filename, loff_t pos, void *buffer,
+ loff_t size, loff_t *actwrite)
+{
+ dir_entry *retdent;
+ fsdata datablock = { .fatbuf = NULL, };
+ fsdata *mydata = &datablock;
+ fat_itr *itr = NULL;
+ int ret = -1;
+ char *filename_copy, *parent, *basename;
+ char l_filename[VFAT_MAXLEN_BYTES];
+
+ debug("writing %s\n", filename);
+
+ filename_copy = strdup(filename);
+ if (!filename_copy)
+ return -ENOMEM;
+
+ split_filename(filename_copy, &parent, &basename);
+ if (!strlen(basename)) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ if (normalize_longname(l_filename, basename)) {
+ printf("FAT: illegal filename (%s)\n", basename);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ itr = malloc_cache_aligned(sizeof(fat_itr));
+ if (!itr) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ ret = fat_itr_root(itr, &datablock);
+ if (ret)
+ goto exit;
+
+ total_sector = datablock.total_sect;
+
+ ret = fat_itr_resolve(itr, parent, TYPE_DIR);
+ if (ret) {
+ printf("%s: doesn't exist (%d)\n", parent, ret);
+ goto exit;
+ }
+
+ retdent = find_directory_entry(itr, l_filename);
+
+ if (retdent) {
+ if (fat_itr_isdir(itr)) {
+ ret = -EISDIR;
+ goto exit;
+ }
+
+ /* A file exists */
+ if (pos == -1)
+ /* Append to the end */
+ pos = FAT2CPU32(retdent->size);
+ if (pos > retdent->size) {
+ /* No hole allowed */
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ /* Update file size in a directory entry */
+ retdent->size = cpu_to_le32(pos + size);
+ } else {
+ /* Create a new file */
+ char shortname[SHORT_NAME_SIZE];
+ int ndent;
+
+ if (pos) {
+ /* No hole allowed */
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ /* Check if long name is needed */
+ ndent = set_name(itr, basename, shortname);
+ if (ndent < 0) {
+ ret = ndent;
+ goto exit;
+ }
+ ret = fat_find_empty_dentries(itr, ndent);
+ if (ret)
+ goto exit;
+ if (ndent > 1) {
+ /* Set long name entries */
+ ret = fill_dir_slot(itr, basename, shortname);
+ if (ret)
+ goto exit;
+ }
+
+ /* Set short name entry */
+ fill_dentry(itr->fsdata, itr->dent, shortname, 0, size,
+ ATTR_ARCH);
+
+ retdent = itr->dent;
+ }
+
+ ret = set_contents(mydata, retdent, pos, buffer, size, actwrite);
+ if (ret < 0) {
+ printf("Error: writing contents\n");
+ ret = -EIO;
+ goto exit;
+ }
+ debug("attempt to write 0x%llx bytes\n", *actwrite);
+
+ /* Flush fat buffer */
+ ret = flush_dirty_fat_buffer(mydata);
+ if (ret) {
+ printf("Error: flush fat buffer\n");
+ ret = -EIO;
+ goto exit;
+ }
+
+ /* Write directory table to device */
+ ret = flush_dir(itr);
+
+exit:
+ free(filename_copy);
+ free(mydata->fatbuf);
+ free(itr);
+ return ret;
+}
+
+int file_fat_write(const char *filename, void *buffer, loff_t offset,
+ loff_t maxsize, loff_t *actwrite)
+{
+ return file_fat_write_at(filename, offset, buffer, maxsize, actwrite);
+}
+
+static int fat_dir_entries(fat_itr *itr)
+{
+ fat_itr *dirs;
+ fsdata fsdata = { .fatbuf = NULL, }, *mydata = &fsdata;
+ /* for FATBUFSIZE */
+ int count;
+
+ dirs = malloc_cache_aligned(sizeof(fat_itr));
+ if (!dirs) {
+ debug("Error: allocating memory\n");
+ count = -ENOMEM;
+ goto exit;
+ }
+
+ /* duplicate fsdata */
+ fat_itr_child(dirs, itr);
+ fsdata = *dirs->fsdata;
+
+ /* allocate local fat buffer */
+ fsdata.fatbuf = malloc_cache_aligned(FATBUFSIZE);
+ if (!fsdata.fatbuf) {
+ debug("Error: allocating memory\n");
+ count = -ENOMEM;
+ goto exit;
+ }
+ fsdata.fatbufnum = -1;
+ dirs->fsdata = &fsdata;
+
+ for (count = 0; fat_itr_next(dirs); count++)
+ ;
+
+exit:
+ free(fsdata.fatbuf);
+ free(dirs);
+ return count;
+}
+
+/**
+ * delete_single_dentry() - delete a single directory entry
+ *
+ * @itr: directory iterator
+ * Return: 0 for success
+ */
+static int delete_single_dentry(fat_itr *itr)
+{
+ struct dir_entry *dent = itr->dent;
+
+ memset(dent, 0, sizeof(*dent));
+ dent->nameext.name[0] = DELETED_FLAG;
+
+ if (!itr->remaining)
+ return flush_dir(itr);
+ return 0;
+}
+
+/**
+ * delete_long_name() - delete long name directory entries
+ *
+ * @itr: directory iterator
+ * Return: 0 for success
+ */
+static int delete_long_name(fat_itr *itr)
+{
+ int seqn = itr->dent->nameext.name[0] & ~LAST_LONG_ENTRY_MASK;
+
+ while (seqn--) {
+ struct dir_entry *dent;
+ int ret;
+
+ ret = delete_single_dentry(itr);
+ if (ret)
+ return ret;
+ dent = next_dent(itr);
+ if (!dent)
+ return -EIO;
+ }
+ return 0;
+}
+
+/**
+ * delete_dentry_long() - remove directory entry
+ *
+ * @itr: directory iterator
+ * Return: 0 for success
+ */
+static int delete_dentry_long(fat_itr *itr)
+{
+ fsdata *mydata = itr->fsdata;
+ dir_entry *dent = itr->dent;
+
+ /* free cluster blocks */
+ clear_fatent(mydata, START(dent));
+ if (flush_dirty_fat_buffer(mydata) < 0) {
+ printf("Error: flush fat buffer\n");
+ return -EIO;
+ }
+ /* Position to first directory entry for long name */
+ if (itr->clust != itr->dent_clust) {
+ int ret;
+
+ ret = fat_move_to_cluster(itr, itr->dent_clust);
+ if (ret)
+ return ret;
+ }
+ itr->dent = itr->dent_start;
+ itr->remaining = itr->dent_rem;
+ dent = itr->dent_start;
+ /* Delete long name */
+ if ((dent->attr & ATTR_VFAT) == ATTR_VFAT &&
+ (dent->nameext.name[0] & LAST_LONG_ENTRY_MASK)) {
+ int ret;
+
+ ret = delete_long_name(itr);
+ if (ret)
+ return ret;
+ }
+ /* Delete short name */
+ delete_single_dentry(itr);
+ return flush_dir(itr);
+}
+
+int fat_unlink(const char *filename)
+{
+ fsdata fsdata = { .fatbuf = NULL, };
+ fat_itr *itr = NULL;
+ int n_entries, ret;
+ char *filename_copy, *dirname, *basename;
+
+ filename_copy = strdup(filename);
+ if (!filename_copy) {
+ printf("Error: allocating memory\n");
+ ret = -ENOMEM;
+ goto exit;
+ }
+ split_filename(filename_copy, &dirname, &basename);
+
+ if (!strcmp(dirname, "/") && !strcmp(basename, "")) {
+ printf("Error: cannot remove root\n");
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ itr = malloc_cache_aligned(sizeof(fat_itr));
+ if (!itr) {
+ printf("Error: allocating memory\n");
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ ret = fat_itr_root(itr, &fsdata);
+ if (ret)
+ goto exit;
+
+ total_sector = fsdata.total_sect;
+
+ ret = fat_itr_resolve(itr, dirname, TYPE_DIR);
+ if (ret) {
+ printf("%s: doesn't exist (%d)\n", dirname, ret);
+ ret = -ENOENT;
+ goto exit;
+ }
+
+ if (!find_directory_entry(itr, basename)) {
+ printf("%s: doesn't exist\n", basename);
+ ret = -ENOENT;
+ goto exit;
+ }
+
+ if (fat_itr_isdir(itr)) {
+ n_entries = fat_dir_entries(itr);
+ if (n_entries < 0) {
+ ret = n_entries;
+ goto exit;
+ }
+ if (n_entries > 2) {
+ printf("Error: directory is not empty: %d\n",
+ n_entries);
+ ret = -EINVAL;
+ goto exit;
+ }
+ }
+
+ ret = delete_dentry_long(itr);
+
+exit:
+ free(fsdata.fatbuf);
+ free(itr);
+ free(filename_copy);
+
+ return ret;
+}
+
+int fat_mkdir(const char *dirname)
+{
+ dir_entry *retdent;
+ fsdata datablock = { .fatbuf = NULL, };
+ fsdata *mydata = &datablock;
+ fat_itr *itr = NULL;
+ char *dirname_copy, *parent, *basename;
+ char l_dirname[VFAT_MAXLEN_BYTES];
+ int ret = -1;
+ loff_t actwrite;
+ unsigned int bytesperclust;
+ dir_entry *dotdent = NULL;
+
+ dirname_copy = strdup(dirname);
+ if (!dirname_copy)
+ goto exit;
+
+ split_filename(dirname_copy, &parent, &basename);
+ if (!strlen(basename)) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ if (normalize_longname(l_dirname, basename)) {
+ printf("FAT: illegal filename (%s)\n", basename);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ itr = malloc_cache_aligned(sizeof(fat_itr));
+ if (!itr) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ ret = fat_itr_root(itr, &datablock);
+ if (ret)
+ goto exit;
+
+ total_sector = datablock.total_sect;
+
+ ret = fat_itr_resolve(itr, parent, TYPE_DIR);
+ if (ret) {
+ printf("%s: doesn't exist (%d)\n", parent, ret);
+ goto exit;
+ }
+
+ retdent = find_directory_entry(itr, l_dirname);
+
+ if (retdent) {
+ printf("%s: already exists\n", l_dirname);
+ ret = -EEXIST;
+ goto exit;
+ } else {
+ char shortname[SHORT_NAME_SIZE];
+ int ndent;
+
+ if (itr->is_root) {
+ /* root dir cannot have "." or ".." */
+ if (!strcmp(l_dirname, ".") ||
+ !strcmp(l_dirname, "..")) {
+ ret = -EINVAL;
+ goto exit;
+ }
+ }
+
+ /* Check if long name is needed */
+ ndent = set_name(itr, basename, shortname);
+ if (ndent < 0) {
+ ret = ndent;
+ goto exit;
+ }
+ ret = fat_find_empty_dentries(itr, ndent);
+ if (ret)
+ goto exit;
+ if (ndent > 1) {
+ /* Set long name entries */
+ ret = fill_dir_slot(itr, basename, shortname);
+ if (ret)
+ goto exit;
+ }
+
+ /* Set attribute as archive for regular file */
+ fill_dentry(itr->fsdata, itr->dent, shortname, 0, 0,
+ ATTR_DIR | ATTR_ARCH);
+
+ retdent = itr->dent;
+ }
+
+ /* Default entries */
+ bytesperclust = mydata->clust_size * mydata->sect_size;
+ dotdent = malloc_cache_aligned(bytesperclust);
+ if (!dotdent) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+ memset(dotdent, 0, bytesperclust);
+
+ memcpy(&dotdent[0].nameext, ". ", 11);
+ dotdent[0].attr = ATTR_DIR | ATTR_ARCH;
+
+ memcpy(&dotdent[1].nameext, ".. ", 11);
+ dotdent[1].attr = ATTR_DIR | ATTR_ARCH;
+
+ if (itr->is_root)
+ set_start_cluster(mydata, &dotdent[1], 0);
+ else
+ set_start_cluster(mydata, &dotdent[1], itr->start_clust);
+
+ ret = set_contents(mydata, retdent, 0, (__u8 *)dotdent,
+ bytesperclust, &actwrite);
+ if (ret < 0) {
+ printf("Error: writing contents\n");
+ goto exit;
+ }
+ /* Write twice for "." */
+ set_start_cluster(mydata, &dotdent[0], START(retdent));
+ ret = set_contents(mydata, retdent, 0, (__u8 *)dotdent,
+ bytesperclust, &actwrite);
+ if (ret < 0) {
+ printf("Error: writing contents\n");
+ goto exit;
+ }
+
+ /* Flush fat buffer */
+ ret = flush_dirty_fat_buffer(mydata);
+ if (ret) {
+ printf("Error: flush fat buffer\n");
+ ret = -EIO;
+ goto exit;
+ }
+
+ /* Write directory table to device */
+ ret = flush_dir(itr);
+
+exit:
+ free(dirname_copy);
+ free(mydata->fatbuf);
+ free(itr);
+ free(dotdent);
+ return ret;
+}
diff --git a/roms/u-boot/fs/fs.c b/roms/u-boot/fs/fs.c
new file mode 100644
index 000000000..b7936fd4c
--- /dev/null
+++ b/roms/u-boot/fs/fs.c
@@ -0,0 +1,958 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ */
+
+#define LOG_CATEGORY LOGC_CORE
+
+#include <command.h>
+#include <config.h>
+#include <errno.h>
+#include <common.h>
+#include <env.h>
+#include <lmb.h>
+#include <log.h>
+#include <mapmem.h>
+#include <part.h>
+#include <ext4fs.h>
+#include <fat.h>
+#include <fs.h>
+#include <sandboxfs.h>
+#include <ubifs_uboot.h>
+#include <btrfs.h>
+#include <asm/global_data.h>
+#include <asm/io.h>
+#include <div64.h>
+#include <linux/math64.h>
+#include <efi_loader.h>
+#include <squashfs.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static struct blk_desc *fs_dev_desc;
+static int fs_dev_part;
+static struct disk_partition fs_partition;
+static int fs_type = FS_TYPE_ANY;
+
+static inline int fs_probe_unsupported(struct blk_desc *fs_dev_desc,
+ struct disk_partition *fs_partition)
+{
+ log_err("** Unrecognized filesystem type **\n");
+ return -1;
+}
+
+static inline int fs_ls_unsupported(const char *dirname)
+{
+ return -1;
+}
+
+/* generic implementation of ls in terms of opendir/readdir/closedir */
+__maybe_unused
+static int fs_ls_generic(const char *dirname)
+{
+ struct fs_dir_stream *dirs;
+ struct fs_dirent *dent;
+ int nfiles = 0, ndirs = 0;
+
+ dirs = fs_opendir(dirname);
+ if (!dirs)
+ return -errno;
+
+ while ((dent = fs_readdir(dirs))) {
+ if (dent->type == FS_DT_DIR) {
+ printf(" %s/\n", dent->name);
+ ndirs++;
+ } else if (dent->type == FS_DT_LNK) {
+ printf(" <SYM> %s\n", dent->name);
+ nfiles++;
+ } else {
+ printf(" %8lld %s\n", dent->size, dent->name);
+ nfiles++;
+ }
+ }
+
+ fs_closedir(dirs);
+
+ printf("\n%d file(s), %d dir(s)\n\n", nfiles, ndirs);
+
+ return 0;
+}
+
+static inline int fs_exists_unsupported(const char *filename)
+{
+ return 0;
+}
+
+static inline int fs_size_unsupported(const char *filename, loff_t *size)
+{
+ return -1;
+}
+
+static inline int fs_read_unsupported(const char *filename, void *buf,
+ loff_t offset, loff_t len,
+ loff_t *actread)
+{
+ return -1;
+}
+
+static inline int fs_write_unsupported(const char *filename, void *buf,
+ loff_t offset, loff_t len,
+ loff_t *actwrite)
+{
+ return -1;
+}
+
+static inline int fs_ln_unsupported(const char *filename, const char *target)
+{
+ return -1;
+}
+
+static inline void fs_close_unsupported(void)
+{
+}
+
+static inline int fs_uuid_unsupported(char *uuid_str)
+{
+ return -1;
+}
+
+static inline int fs_opendir_unsupported(const char *filename,
+ struct fs_dir_stream **dirs)
+{
+ return -EACCES;
+}
+
+static inline int fs_unlink_unsupported(const char *filename)
+{
+ return -1;
+}
+
+static inline int fs_mkdir_unsupported(const char *dirname)
+{
+ return -1;
+}
+
+struct fstype_info {
+ int fstype;
+ char *name;
+ /*
+ * Is it legal to pass NULL as .probe()'s fs_dev_desc parameter? This
+ * should be false in most cases. For "virtual" filesystems which
+ * aren't based on a U-Boot block device (e.g. sandbox), this can be
+ * set to true. This should also be true for the dummy entry at the end
+ * of fstypes[], since that is essentially a "virtual" (non-existent)
+ * filesystem.
+ */
+ bool null_dev_desc_ok;
+ int (*probe)(struct blk_desc *fs_dev_desc,
+ struct disk_partition *fs_partition);
+ int (*ls)(const char *dirname);
+ int (*exists)(const char *filename);
+ int (*size)(const char *filename, loff_t *size);
+ int (*read)(const char *filename, void *buf, loff_t offset,
+ loff_t len, loff_t *actread);
+ int (*write)(const char *filename, void *buf, loff_t offset,
+ loff_t len, loff_t *actwrite);
+ void (*close)(void);
+ int (*uuid)(char *uuid_str);
+ /*
+ * Open a directory stream. On success return 0 and directory
+ * stream pointer via 'dirsp'. On error, return -errno. See
+ * fs_opendir().
+ */
+ int (*opendir)(const char *filename, struct fs_dir_stream **dirsp);
+ /*
+ * Read next entry from directory stream. On success return 0
+ * and directory entry pointer via 'dentp'. On error return
+ * -errno. See fs_readdir().
+ */
+ int (*readdir)(struct fs_dir_stream *dirs, struct fs_dirent **dentp);
+ /* see fs_closedir() */
+ void (*closedir)(struct fs_dir_stream *dirs);
+ int (*unlink)(const char *filename);
+ int (*mkdir)(const char *dirname);
+ int (*ln)(const char *filename, const char *target);
+};
+
+static struct fstype_info fstypes[] = {
+#ifdef CONFIG_FS_FAT
+ {
+ .fstype = FS_TYPE_FAT,
+ .name = "fat",
+ .null_dev_desc_ok = false,
+ .probe = fat_set_blk_dev,
+ .close = fat_close,
+ .ls = fs_ls_generic,
+ .exists = fat_exists,
+ .size = fat_size,
+ .read = fat_read_file,
+#if CONFIG_IS_ENABLED(FAT_WRITE)
+ .write = file_fat_write,
+ .unlink = fat_unlink,
+ .mkdir = fat_mkdir,
+#else
+ .write = fs_write_unsupported,
+ .unlink = fs_unlink_unsupported,
+ .mkdir = fs_mkdir_unsupported,
+#endif
+ .uuid = fat_uuid,
+ .opendir = fat_opendir,
+ .readdir = fat_readdir,
+ .closedir = fat_closedir,
+ .ln = fs_ln_unsupported,
+ },
+#endif
+
+#if CONFIG_IS_ENABLED(FS_EXT4)
+ {
+ .fstype = FS_TYPE_EXT,
+ .name = "ext4",
+ .null_dev_desc_ok = false,
+ .probe = ext4fs_probe,
+ .close = ext4fs_close,
+ .ls = ext4fs_ls,
+ .exists = ext4fs_exists,
+ .size = ext4fs_size,
+ .read = ext4_read_file,
+#ifdef CONFIG_CMD_EXT4_WRITE
+ .write = ext4_write_file,
+ .ln = ext4fs_create_link,
+#else
+ .write = fs_write_unsupported,
+ .ln = fs_ln_unsupported,
+#endif
+ .uuid = ext4fs_uuid,
+ .opendir = fs_opendir_unsupported,
+ .unlink = fs_unlink_unsupported,
+ .mkdir = fs_mkdir_unsupported,
+ },
+#endif
+#ifdef CONFIG_SANDBOX
+ {
+ .fstype = FS_TYPE_SANDBOX,
+ .name = "sandbox",
+ .null_dev_desc_ok = true,
+ .probe = sandbox_fs_set_blk_dev,
+ .close = sandbox_fs_close,
+ .ls = sandbox_fs_ls,
+ .exists = sandbox_fs_exists,
+ .size = sandbox_fs_size,
+ .read = fs_read_sandbox,
+ .write = fs_write_sandbox,
+ .uuid = fs_uuid_unsupported,
+ .opendir = fs_opendir_unsupported,
+ .unlink = fs_unlink_unsupported,
+ .mkdir = fs_mkdir_unsupported,
+ .ln = fs_ln_unsupported,
+ },
+#endif
+#ifdef CONFIG_CMD_UBIFS
+ {
+ .fstype = FS_TYPE_UBIFS,
+ .name = "ubifs",
+ .null_dev_desc_ok = true,
+ .probe = ubifs_set_blk_dev,
+ .close = ubifs_close,
+ .ls = ubifs_ls,
+ .exists = ubifs_exists,
+ .size = ubifs_size,
+ .read = ubifs_read,
+ .write = fs_write_unsupported,
+ .uuid = fs_uuid_unsupported,
+ .opendir = fs_opendir_unsupported,
+ .unlink = fs_unlink_unsupported,
+ .mkdir = fs_mkdir_unsupported,
+ .ln = fs_ln_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,
+ .opendir = fs_opendir_unsupported,
+ .unlink = fs_unlink_unsupported,
+ .mkdir = fs_mkdir_unsupported,
+ .ln = fs_ln_unsupported,
+ },
+#endif
+#if IS_ENABLED(CONFIG_FS_SQUASHFS)
+ {
+ .fstype = FS_TYPE_SQUASHFS,
+ .name = "squashfs",
+ .null_dev_desc_ok = false,
+ .probe = sqfs_probe,
+ .opendir = sqfs_opendir,
+ .readdir = sqfs_readdir,
+ .ls = fs_ls_generic,
+ .read = sqfs_read,
+ .size = sqfs_size,
+ .close = sqfs_close,
+ .closedir = sqfs_closedir,
+ .exists = sqfs_exists,
+ .uuid = fs_uuid_unsupported,
+ .write = fs_write_unsupported,
+ .ln = fs_ln_unsupported,
+ .unlink = fs_unlink_unsupported,
+ .mkdir = fs_mkdir_unsupported,
+ },
+#endif
+ {
+ .fstype = FS_TYPE_ANY,
+ .name = "unsupported",
+ .null_dev_desc_ok = true,
+ .probe = fs_probe_unsupported,
+ .close = fs_close_unsupported,
+ .ls = fs_ls_unsupported,
+ .exists = fs_exists_unsupported,
+ .size = fs_size_unsupported,
+ .read = fs_read_unsupported,
+ .write = fs_write_unsupported,
+ .uuid = fs_uuid_unsupported,
+ .opendir = fs_opendir_unsupported,
+ .unlink = fs_unlink_unsupported,
+ .mkdir = fs_mkdir_unsupported,
+ .ln = fs_ln_unsupported,
+ },
+};
+
+static struct fstype_info *fs_get_info(int fstype)
+{
+ struct fstype_info *info;
+ int i;
+
+ for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes) - 1; i++, info++) {
+ if (fstype == info->fstype)
+ return info;
+ }
+
+ /* Return the 'unsupported' sentinel */
+ return info;
+}
+
+/**
+ * fs_get_type() - Get type of current filesystem
+ *
+ * Return: filesystem type
+ *
+ * Returns filesystem type representing the current filesystem, or
+ * FS_TYPE_ANY for any unrecognised filesystem.
+ */
+int fs_get_type(void)
+{
+ return fs_type;
+}
+
+/**
+ * fs_get_type_name() - Get type of current filesystem
+ *
+ * Return: Pointer to filesystem name
+ *
+ * Returns a string describing the current filesystem, or the sentinel
+ * "unsupported" for any unrecognised filesystem.
+ */
+const char *fs_get_type_name(void)
+{
+ return fs_get_info(fs_type)->name;
+}
+
+int fs_set_blk_dev(const char *ifname, const char *dev_part_str, int fstype)
+{
+ struct fstype_info *info;
+ int part, i;
+#ifdef CONFIG_NEEDS_MANUAL_RELOC
+ static int relocated;
+
+ if (!relocated) {
+ for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes);
+ i++, info++) {
+ info->name += gd->reloc_off;
+ info->probe += gd->reloc_off;
+ info->close += gd->reloc_off;
+ info->ls += gd->reloc_off;
+ info->read += gd->reloc_off;
+ info->write += gd->reloc_off;
+ }
+ relocated = 1;
+ }
+#endif
+
+ part = part_get_info_by_dev_and_name_or_num(ifname, dev_part_str, &fs_dev_desc,
+ &fs_partition, 1);
+ if (part < 0)
+ return -1;
+
+ for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes); i++, info++) {
+ if (fstype != FS_TYPE_ANY && info->fstype != FS_TYPE_ANY &&
+ fstype != info->fstype)
+ continue;
+
+ if (!fs_dev_desc && !info->null_dev_desc_ok)
+ continue;
+
+ if (!info->probe(fs_dev_desc, &fs_partition)) {
+ fs_type = info->fstype;
+ fs_dev_part = part;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+/* set current blk device w/ blk_desc + partition # */
+int fs_set_blk_dev_with_part(struct blk_desc *desc, int part)
+{
+ struct fstype_info *info;
+ int ret, i;
+
+ if (part >= 1)
+ ret = part_get_info(desc, part, &fs_partition);
+ else
+ ret = part_get_info_whole_disk(desc, &fs_partition);
+ if (ret)
+ return ret;
+ fs_dev_desc = desc;
+
+ for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes); i++, info++) {
+ if (!info->probe(fs_dev_desc, &fs_partition)) {
+ fs_type = info->fstype;
+ fs_dev_part = part;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+void fs_close(void)
+{
+ struct fstype_info *info = fs_get_info(fs_type);
+
+ info->close();
+
+ fs_type = FS_TYPE_ANY;
+}
+
+int fs_uuid(char *uuid_str)
+{
+ struct fstype_info *info = fs_get_info(fs_type);
+
+ return info->uuid(uuid_str);
+}
+
+int fs_ls(const char *dirname)
+{
+ int ret;
+
+ struct fstype_info *info = fs_get_info(fs_type);
+
+ ret = info->ls(dirname);
+
+ fs_close();
+
+ return ret;
+}
+
+int fs_exists(const char *filename)
+{
+ int ret;
+
+ struct fstype_info *info = fs_get_info(fs_type);
+
+ ret = info->exists(filename);
+
+ fs_close();
+
+ return ret;
+}
+
+int fs_size(const char *filename, loff_t *size)
+{
+ int ret;
+
+ struct fstype_info *info = fs_get_info(fs_type);
+
+ ret = info->size(filename, size);
+
+ fs_close();
+
+ return ret;
+}
+
+#ifdef CONFIG_LMB
+/* Check if a file may be read to the given address */
+static int fs_read_lmb_check(const char *filename, ulong addr, loff_t offset,
+ loff_t len, struct fstype_info *info)
+{
+ struct lmb lmb;
+ int ret;
+ loff_t size;
+ loff_t read_len;
+
+ /* get the actual size of the file */
+ ret = info->size(filename, &size);
+ if (ret)
+ return ret;
+ if (offset >= size) {
+ /* offset >= EOF, no bytes will be written */
+ return 0;
+ }
+ read_len = size - offset;
+
+ /* limit to 'len' if it is smaller */
+ if (len && len < read_len)
+ read_len = len;
+
+ lmb_init_and_reserve(&lmb, gd->bd, (void *)gd->fdt_blob);
+ lmb_dump_all(&lmb);
+
+ if (lmb_alloc_addr(&lmb, addr, read_len) == addr)
+ return 0;
+
+ log_err("** Reading file would overwrite reserved memory **\n");
+ return -ENOSPC;
+}
+#endif
+
+static int _fs_read(const char *filename, ulong addr, loff_t offset, loff_t len,
+ int do_lmb_check, loff_t *actread)
+{
+ struct fstype_info *info = fs_get_info(fs_type);
+ void *buf;
+ int ret;
+
+#ifdef CONFIG_LMB
+ if (do_lmb_check) {
+ ret = fs_read_lmb_check(filename, addr, offset, len, info);
+ if (ret)
+ return ret;
+ }
+#endif
+
+ /*
+ * We don't actually know how many bytes are being read, since len==0
+ * means read the whole file.
+ */
+ buf = map_sysmem(addr, len);
+ ret = info->read(filename, buf, offset, len, actread);
+ unmap_sysmem(buf);
+
+ /* If we requested a specific number of bytes, check we got it */
+ if (ret == 0 && len && *actread != len)
+ log_debug("** %s shorter than offset + len **\n", filename);
+ fs_close();
+
+ return ret;
+}
+
+int fs_read(const char *filename, ulong addr, loff_t offset, loff_t len,
+ loff_t *actread)
+{
+ return _fs_read(filename, addr, offset, len, 0, actread);
+}
+
+int fs_write(const char *filename, ulong addr, loff_t offset, loff_t len,
+ loff_t *actwrite)
+{
+ struct fstype_info *info = fs_get_info(fs_type);
+ void *buf;
+ int ret;
+
+ buf = map_sysmem(addr, len);
+ ret = info->write(filename, buf, offset, len, actwrite);
+ unmap_sysmem(buf);
+
+ if (ret < 0 && len != *actwrite) {
+ log_err("** Unable to write file %s **\n", filename);
+ ret = -1;
+ }
+ fs_close();
+
+ return ret;
+}
+
+struct fs_dir_stream *fs_opendir(const char *filename)
+{
+ struct fstype_info *info = fs_get_info(fs_type);
+ struct fs_dir_stream *dirs = NULL;
+ int ret;
+
+ ret = info->opendir(filename, &dirs);
+ fs_close();
+ if (ret) {
+ errno = -ret;
+ return NULL;
+ }
+
+ dirs->desc = fs_dev_desc;
+ dirs->part = fs_dev_part;
+
+ return dirs;
+}
+
+struct fs_dirent *fs_readdir(struct fs_dir_stream *dirs)
+{
+ struct fstype_info *info;
+ struct fs_dirent *dirent;
+ int ret;
+
+ fs_set_blk_dev_with_part(dirs->desc, dirs->part);
+ info = fs_get_info(fs_type);
+
+ ret = info->readdir(dirs, &dirent);
+ fs_close();
+ if (ret) {
+ errno = -ret;
+ return NULL;
+ }
+
+ return dirent;
+}
+
+void fs_closedir(struct fs_dir_stream *dirs)
+{
+ struct fstype_info *info;
+
+ if (!dirs)
+ return;
+
+ fs_set_blk_dev_with_part(dirs->desc, dirs->part);
+ info = fs_get_info(fs_type);
+
+ info->closedir(dirs);
+ fs_close();
+}
+
+int fs_unlink(const char *filename)
+{
+ int ret;
+
+ struct fstype_info *info = fs_get_info(fs_type);
+
+ ret = info->unlink(filename);
+
+ fs_close();
+
+ return ret;
+}
+
+int fs_mkdir(const char *dirname)
+{
+ int ret;
+
+ struct fstype_info *info = fs_get_info(fs_type);
+
+ ret = info->mkdir(dirname);
+
+ fs_close();
+
+ return ret;
+}
+
+int fs_ln(const char *fname, const char *target)
+{
+ struct fstype_info *info = fs_get_info(fs_type);
+ int ret;
+
+ ret = info->ln(fname, target);
+
+ if (ret < 0) {
+ log_err("** Unable to create link %s -> %s **\n", fname, target);
+ ret = -1;
+ }
+ fs_close();
+
+ return ret;
+}
+
+int do_size(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[],
+ int fstype)
+{
+ loff_t size;
+
+ if (argc != 4)
+ return CMD_RET_USAGE;
+
+ if (fs_set_blk_dev(argv[1], argv[2], fstype))
+ return 1;
+
+ if (fs_size(argv[3], &size) < 0)
+ return CMD_RET_FAILURE;
+
+ env_set_hex("filesize", size);
+
+ return 0;
+}
+
+int do_load(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[],
+ int fstype)
+{
+ unsigned long addr;
+ const char *addr_str;
+ const char *filename;
+ loff_t bytes;
+ loff_t pos;
+ loff_t len_read;
+ int ret;
+ unsigned long time;
+ char *ep;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+ if (argc > 7)
+ return CMD_RET_USAGE;
+
+ if (fs_set_blk_dev(argv[1], (argc >= 3) ? argv[2] : NULL, fstype)) {
+ log_err("Can't set block device\n");
+ return 1;
+ }
+
+ if (argc >= 4) {
+ addr = simple_strtoul(argv[3], &ep, 16);
+ if (ep == argv[3] || *ep != '\0')
+ return CMD_RET_USAGE;
+ } else {
+ addr_str = env_get("loadaddr");
+ if (addr_str != NULL)
+ addr = simple_strtoul(addr_str, NULL, 16);
+ else
+ addr = CONFIG_SYS_LOAD_ADDR;
+ }
+ if (argc >= 5) {
+ filename = argv[4];
+ } else {
+ filename = env_get("bootfile");
+ if (!filename) {
+ puts("** No boot file defined **\n");
+ return 1;
+ }
+ }
+ if (argc >= 6)
+ bytes = simple_strtoul(argv[5], NULL, 16);
+ else
+ bytes = 0;
+ if (argc >= 7)
+ pos = simple_strtoul(argv[6], NULL, 16);
+ else
+ pos = 0;
+
+ time = get_timer(0);
+ ret = _fs_read(filename, addr, pos, bytes, 1, &len_read);
+ time = get_timer(time);
+ if (ret < 0) {
+ log_err("Failed to load '%s'\n", filename);
+ return 1;
+ }
+
+ if (IS_ENABLED(CONFIG_CMD_BOOTEFI))
+ efi_set_bootdev(argv[1], (argc > 2) ? argv[2] : "",
+ (argc > 4) ? argv[4] : "", map_sysmem(addr, 0),
+ len_read);
+
+ printf("%llu bytes read in %lu ms", len_read, time);
+ if (time > 0) {
+ puts(" (");
+ print_size(div_u64(len_read, time) * 1000, "/s");
+ puts(")");
+ }
+ puts("\n");
+
+ env_set_hex("fileaddr", addr);
+ env_set_hex("filesize", len_read);
+
+ return 0;
+}
+
+int do_ls(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[],
+ int fstype)
+{
+ if (argc < 2)
+ return CMD_RET_USAGE;
+ if (argc > 4)
+ return CMD_RET_USAGE;
+
+ if (fs_set_blk_dev(argv[1], (argc >= 3) ? argv[2] : NULL, fstype))
+ return 1;
+
+ if (fs_ls(argc >= 4 ? argv[3] : "/"))
+ return 1;
+
+ return 0;
+}
+
+int file_exists(const char *dev_type, const char *dev_part, const char *file,
+ int fstype)
+{
+ if (fs_set_blk_dev(dev_type, dev_part, fstype))
+ return 0;
+
+ return fs_exists(file);
+}
+
+int do_save(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[],
+ int fstype)
+{
+ unsigned long addr;
+ const char *filename;
+ loff_t bytes;
+ loff_t pos;
+ loff_t len;
+ int ret;
+ unsigned long time;
+
+ if (argc < 6 || argc > 7)
+ return CMD_RET_USAGE;
+
+ if (fs_set_blk_dev(argv[1], argv[2], fstype))
+ return 1;
+
+ addr = simple_strtoul(argv[3], NULL, 16);
+ filename = argv[4];
+ bytes = simple_strtoul(argv[5], NULL, 16);
+ if (argc >= 7)
+ pos = simple_strtoul(argv[6], NULL, 16);
+ else
+ pos = 0;
+
+ time = get_timer(0);
+ ret = fs_write(filename, addr, pos, bytes, &len);
+ time = get_timer(time);
+ if (ret < 0)
+ return 1;
+
+ printf("%llu bytes written in %lu ms", len, time);
+ if (time > 0) {
+ puts(" (");
+ print_size(div_u64(len, time) * 1000, "/s");
+ puts(")");
+ }
+ puts("\n");
+
+ return 0;
+}
+
+int do_fs_uuid(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[],
+ int fstype)
+{
+ int ret;
+ char uuid[37];
+ memset(uuid, 0, sizeof(uuid));
+
+ if (argc < 3 || argc > 4)
+ return CMD_RET_USAGE;
+
+ if (fs_set_blk_dev(argv[1], argv[2], fstype))
+ return 1;
+
+ ret = fs_uuid(uuid);
+ if (ret)
+ return CMD_RET_FAILURE;
+
+ if (argc == 4)
+ env_set(argv[3], uuid);
+ else
+ printf("%s\n", uuid);
+
+ return CMD_RET_SUCCESS;
+}
+
+int do_fs_type(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct fstype_info *info;
+
+ if (argc < 3 || argc > 4)
+ return CMD_RET_USAGE;
+
+ if (fs_set_blk_dev(argv[1], argv[2], FS_TYPE_ANY))
+ return 1;
+
+ info = fs_get_info(fs_type);
+
+ if (argc == 4)
+ env_set(argv[3], info->name);
+ else
+ printf("%s\n", info->name);
+
+ fs_close();
+
+ return CMD_RET_SUCCESS;
+}
+
+int do_rm(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[],
+ int fstype)
+{
+ if (argc != 4)
+ return CMD_RET_USAGE;
+
+ if (fs_set_blk_dev(argv[1], argv[2], fstype))
+ return 1;
+
+ if (fs_unlink(argv[3]))
+ return 1;
+
+ return 0;
+}
+
+int do_mkdir(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[],
+ int fstype)
+{
+ int ret;
+
+ if (argc != 4)
+ return CMD_RET_USAGE;
+
+ if (fs_set_blk_dev(argv[1], argv[2], fstype))
+ return 1;
+
+ ret = fs_mkdir(argv[3]);
+ if (ret) {
+ log_err("** Unable to create a directory \"%s\" **\n", argv[3]);
+ return 1;
+ }
+
+ return 0;
+}
+
+int do_ln(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[],
+ int fstype)
+{
+ if (argc != 5)
+ return CMD_RET_USAGE;
+
+ if (fs_set_blk_dev(argv[1], argv[2], fstype))
+ return 1;
+
+ if (fs_ln(argv[3], argv[4]))
+ return 1;
+
+ return 0;
+}
+
+int do_fs_types(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
+{
+ struct fstype_info *drv = fstypes;
+ const int n_ents = ARRAY_SIZE(fstypes);
+ struct fstype_info *entry;
+ int i = 0;
+
+ puts("Supported filesystems");
+ for (entry = drv; entry != drv + n_ents; entry++) {
+ if (entry->fstype != FS_TYPE_ANY) {
+ printf("%c %s", i ? ',' : ':', entry->name);
+ i++;
+ }
+ }
+ if (!i)
+ puts(": <none>");
+ puts("\n");
+ return CMD_RET_SUCCESS;
+}
diff --git a/roms/u-boot/fs/fs_internal.c b/roms/u-boot/fs/fs_internal.c
new file mode 100644
index 000000000..bfc35c996
--- /dev/null
+++ b/roms/u-boot/fs/fs_internal.c
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * 2017 by Marek Behun <marek.behun@nic.cz>
+ *
+ * Derived from code in ext4/dev.c, which was based on reiserfs/dev.c
+ */
+
+#define LOG_CATEGORY LOGC_CORE
+
+#include <common.h>
+#include <blk.h>
+#include <compiler.h>
+#include <log.h>
+#include <part.h>
+#include <memalign.h>
+
+int fs_devread(struct blk_desc *blk, struct disk_partition *partition,
+ lbaint_t sector, int byte_offset, int byte_len, char *buf)
+{
+ unsigned block_len;
+ int log2blksz;
+ ALLOC_CACHE_ALIGN_BUFFER(char, sec_buf, (blk ? blk->blksz : 0));
+ if (blk == NULL) {
+ log_err("** Invalid Block Device Descriptor (NULL)\n");
+ return 0;
+ }
+ log2blksz = blk->log2blksz;
+
+ /* Check partition boundaries */
+ if ((sector + ((byte_offset + byte_len - 1) >> log2blksz))
+ >= partition->size) {
+ log_err("%s read outside partition " LBAFU "\n", __func__,
+ sector);
+ return 0;
+ }
+
+ /* Get the read to the beginning of a partition */
+ sector += byte_offset >> log2blksz;
+ byte_offset &= blk->blksz - 1;
+
+ log_debug(" <" LBAFU ", %d, %d>\n", sector, byte_offset, byte_len);
+
+ if (byte_offset != 0) {
+ int readlen;
+ /* read first part which isn't aligned with start of sector */
+ if (blk_dread(blk, partition->start + sector, 1,
+ (void *)sec_buf) != 1) {
+ log_err(" ** %s read error **\n", __func__);
+ return 0;
+ }
+ readlen = min((int)blk->blksz - byte_offset,
+ byte_len);
+ memcpy(buf, sec_buf + byte_offset, readlen);
+ buf += readlen;
+ byte_len -= readlen;
+ sector++;
+ }
+
+ if (byte_len == 0)
+ return 1;
+
+ /* read sector aligned part */
+ block_len = byte_len & ~(blk->blksz - 1);
+
+ if (block_len == 0) {
+ ALLOC_CACHE_ALIGN_BUFFER(u8, p, blk->blksz);
+
+ block_len = blk->blksz;
+ blk_dread(blk, partition->start + sector, 1,
+ (void *)p);
+ memcpy(buf, p, byte_len);
+ return 1;
+ }
+
+ if (blk_dread(blk, partition->start + sector,
+ block_len >> log2blksz, (void *)buf) !=
+ block_len >> log2blksz) {
+ log_err(" ** %s read error - block\n", __func__);
+ return 0;
+ }
+ block_len = byte_len & ~(blk->blksz - 1);
+ buf += block_len;
+ byte_len -= block_len;
+ sector += block_len / blk->blksz;
+
+ if (byte_len != 0) {
+ /* read rest of data which are not in whole sector */
+ if (blk_dread(blk, partition->start + sector, 1,
+ (void *)sec_buf) != 1) {
+ log_err("* %s read error - last part\n", __func__);
+ return 0;
+ }
+ memcpy(buf, sec_buf, byte_len);
+ }
+ return 1;
+}
diff --git a/roms/u-boot/fs/jffs2/Kconfig b/roms/u-boot/fs/jffs2/Kconfig
new file mode 100644
index 000000000..1b9ecdd8c
--- /dev/null
+++ b/roms/u-boot/fs/jffs2/Kconfig
@@ -0,0 +1,7 @@
+config FS_JFFS2
+ bool "Enable JFFS2 filesystem support"
+ help
+ This provides support for reading images from JFFS2 (Journalling
+ Flash File System version 2). JFFS2 is a log-structured file system
+ for use with flash memory devices. It supports raw NAND devices,
+ hard links and compression.
diff --git a/roms/u-boot/fs/jffs2/LICENCE b/roms/u-boot/fs/jffs2/LICENCE
new file mode 100644
index 000000000..562885908
--- /dev/null
+++ b/roms/u-boot/fs/jffs2/LICENCE
@@ -0,0 +1,30 @@
+The files in this directory and elsewhere which refer to this LICENCE
+file are part of JFFS2, the Journalling Flash File System v2.
+
+ Copyright © 2001-2007 Red Hat, Inc. and others
+
+JFFS2 is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 or (at your option) any later
+version.
+
+JFFS2 is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License along
+with JFFS2; if not, write to the Free Software Foundation, Inc.,
+59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+
+As a special exception, if other files instantiate templates or use
+macros or inline functions from these files, or you compile these
+files and link them with other works to produce a work based on these
+files, these files do not by themselves cause the resulting work to be
+covered by the GNU General Public License. However the source code for
+these files must still be made available in accordance with section (3)
+of the GNU General Public License.
+
+This exception does not invalidate any other reasons why a work based on
+this file might be covered by the GNU General Public License.
+
diff --git a/roms/u-boot/fs/jffs2/Makefile b/roms/u-boot/fs/jffs2/Makefile
new file mode 100644
index 000000000..ec7428c84
--- /dev/null
+++ b/roms/u-boot/fs/jffs2/Makefile
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright 2000-2006
+# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+
+obj-$(CONFIG_JFFS2_LZO) += compr_lzo.o
+obj-y += compr_rtime.o
+obj-y += compr_rubin.o
+obj-y += compr_zlib.o
+obj-y += jffs2_1pass.o
+obj-$(CONFIG_SYS_JFFS2_SORT_FRAGMENTS) += mergesort.o
+obj-y += mini_inflate.o
diff --git a/roms/u-boot/fs/jffs2/compr_lzo.c b/roms/u-boot/fs/jffs2/compr_lzo.c
new file mode 100644
index 000000000..e648ec4fb
--- /dev/null
+++ b/roms/u-boot/fs/jffs2/compr_lzo.c
@@ -0,0 +1,401 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2004 Patrik Kluba,
+ * University of Szeged, Hungary
+ *
+ * For licensing information, see the file 'LICENCE' in the
+ * jffs2 directory.
+ *
+ * $Id: compr_lzo.c,v 1.3 2004/06/23 16:34:39 havasi Exp $
+ *
+ */
+
+/*
+ LZO1X-1 (and -999) compression module for jffs2
+ based on the original LZO sources
+*/
+
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+
+/*
+ Original copyright notice follows:
+
+ lzo1x_9x.c -- implementation of the LZO1X-999 compression algorithm
+ lzo_ptr.h -- low-level pointer constructs
+ lzo_swd.ch -- sliding window dictionary
+ lzoconf.h -- configuration for the LZO real-time data compression library
+ lzo_mchw.ch -- matching functions using a window
+ minilzo.c -- mini subset of the LZO real-time data compression library
+ config1x.h -- configuration for the LZO1X algorithm
+ lzo1x.h -- public interface of the LZO1X compression algorithm
+
+ These files are part of the LZO real-time data compression library.
+
+ Copyright (C) 1996-2002 Markus Franz Xaver Johannes Oberhumer
+ All Rights Reserved.
+
+ The LZO library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License, or (at your option) any later version.
+
+ The LZO library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the LZO library; see the file COPYING.
+ If not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Markus F.X.J. Oberhumer
+ <markus@oberhumer.com>
+*/
+
+/*
+
+ 2004-02-16 pajko <pajko(AT)halom(DOT)u-szeged(DOT)hu>
+ Initial release
+ -removed all 16 bit code
+ -all sensitive data will be on 4 byte boundary
+ -removed check parts for library use
+ -removed all but LZO1X-* compression
+
+*/
+
+
+#include <config.h>
+#include <linux/stddef.h>
+#include <jffs2/jffs2.h>
+#include <jffs2/compr_rubin.h>
+
+/* Integral types that have *exactly* the same number of bits as a lzo_voidp */
+typedef unsigned long lzo_ptr_t;
+typedef long lzo_sptr_t;
+
+/* data type definitions */
+#define U32 unsigned long
+#define S32 signed long
+#define I32 long
+#define U16 unsigned short
+#define S16 signed short
+#define I16 short
+#define U8 unsigned char
+#define S8 signed char
+#define I8 char
+
+#define M1_MAX_OFFSET 0x0400
+#define M2_MAX_OFFSET 0x0800
+#define M3_MAX_OFFSET 0x4000
+#define M4_MAX_OFFSET 0xbfff
+
+#define __COPY4(dst,src) * (lzo_uint32p)(dst) = * (const lzo_uint32p)(src)
+#define COPY4(dst,src) __COPY4((lzo_ptr_t)(dst),(lzo_ptr_t)(src))
+
+#define TEST_IP (ip < ip_end)
+#define TEST_OP (op <= op_end)
+
+#define NEED_IP(x) \
+ if ((lzo_uint)(ip_end - ip) < (lzo_uint)(x)) goto input_overrun
+#define NEED_OP(x) \
+ if ((lzo_uint)(op_end - op) < (lzo_uint)(x)) goto output_overrun
+#define TEST_LOOKBEHIND(m_pos,out) if (m_pos < out) goto lookbehind_overrun
+
+typedef U32 lzo_uint32;
+typedef I32 lzo_int32;
+typedef U32 lzo_uint;
+typedef I32 lzo_int;
+typedef int lzo_bool;
+
+#define lzo_byte U8
+#define lzo_bytep U8 *
+#define lzo_charp char *
+#define lzo_voidp void *
+#define lzo_shortp short *
+#define lzo_ushortp unsigned short *
+#define lzo_uint32p lzo_uint32 *
+#define lzo_int32p lzo_int32 *
+#define lzo_uintp lzo_uint *
+#define lzo_intp lzo_int *
+#define lzo_voidpp lzo_voidp *
+#define lzo_bytepp lzo_bytep *
+#define lzo_sizeof_dict_t sizeof(lzo_bytep)
+
+#define LZO_E_OK 0
+#define LZO_E_ERROR (-1)
+#define LZO_E_OUT_OF_MEMORY (-2) /* not used right now */
+#define LZO_E_NOT_COMPRESSIBLE (-3) /* not used right now */
+#define LZO_E_INPUT_OVERRUN (-4)
+#define LZO_E_OUTPUT_OVERRUN (-5)
+#define LZO_E_LOOKBEHIND_OVERRUN (-6)
+#define LZO_E_EOF_NOT_FOUND (-7)
+#define LZO_E_INPUT_NOT_CONSUMED (-8)
+
+#define PTR(a) ((lzo_ptr_t) (a))
+#define PTR_LINEAR(a) PTR(a)
+#define PTR_ALIGNED_4(a) ((PTR_LINEAR(a) & 3) == 0)
+#define PTR_ALIGNED_8(a) ((PTR_LINEAR(a) & 7) == 0)
+#define PTR_ALIGNED2_4(a,b) (((PTR_LINEAR(a) | PTR_LINEAR(b)) & 3) == 0)
+#define PTR_ALIGNED2_8(a,b) (((PTR_LINEAR(a) | PTR_LINEAR(b)) & 7) == 0)
+#define PTR_LT(a,b) (PTR(a) < PTR(b))
+#define PTR_GE(a,b) (PTR(a) >= PTR(b))
+#define PTR_DIFF(a,b) ((lzo_ptrdiff_t) (PTR(a) - PTR(b)))
+#define pd(a,b) ((lzo_uint) ((a)-(b)))
+
+typedef ptrdiff_t lzo_ptrdiff_t;
+
+static int
+lzo1x_decompress (const lzo_byte * in, lzo_uint in_len,
+ lzo_byte * out, lzo_uintp out_len, lzo_voidp wrkmem)
+{
+ register lzo_byte *op;
+ register const lzo_byte *ip;
+ register lzo_uint t;
+
+ register const lzo_byte *m_pos;
+
+ const lzo_byte *const ip_end = in + in_len;
+ lzo_byte *const op_end = out + *out_len;
+
+ *out_len = 0;
+
+ op = out;
+ ip = in;
+
+ if (*ip > 17)
+ {
+ t = *ip++ - 17;
+ if (t < 4)
+ goto match_next;
+ NEED_OP (t);
+ NEED_IP (t + 1);
+ do
+ *op++ = *ip++;
+ while (--t > 0);
+ goto first_literal_run;
+ }
+
+ while (TEST_IP && TEST_OP)
+ {
+ t = *ip++;
+ if (t >= 16)
+ goto match;
+ if (t == 0)
+ {
+ NEED_IP (1);
+ while (*ip == 0)
+ {
+ t += 255;
+ ip++;
+ NEED_IP (1);
+ }
+ t += 15 + *ip++;
+ }
+ NEED_OP (t + 3);
+ NEED_IP (t + 4);
+ if (PTR_ALIGNED2_4 (op, ip))
+ {
+ COPY4 (op, ip);
+
+ op += 4;
+ ip += 4;
+ if (--t > 0)
+ {
+ if (t >= 4)
+ {
+ do
+ {
+ COPY4 (op, ip);
+ op += 4;
+ ip += 4;
+ t -= 4;
+ }
+ while (t >= 4);
+ if (t > 0)
+ do
+ *op++ = *ip++;
+ while (--t > 0);
+ }
+ else
+ do
+ *op++ = *ip++;
+ while (--t > 0);
+ }
+ }
+ else
+ {
+ *op++ = *ip++;
+ *op++ = *ip++;
+ *op++ = *ip++;
+ do
+ *op++ = *ip++;
+ while (--t > 0);
+ }
+ first_literal_run:
+
+ t = *ip++;
+ if (t >= 16)
+ goto match;
+
+ m_pos = op - (1 + M2_MAX_OFFSET);
+ m_pos -= t >> 2;
+ m_pos -= *ip++ << 2;
+ TEST_LOOKBEHIND (m_pos, out);
+ NEED_OP (3);
+ *op++ = *m_pos++;
+ *op++ = *m_pos++;
+ *op++ = *m_pos;
+
+ goto match_done;
+
+ while (TEST_IP && TEST_OP)
+ {
+ match:
+ if (t >= 64)
+ {
+ m_pos = op - 1;
+ m_pos -= (t >> 2) & 7;
+ m_pos -= *ip++ << 3;
+ t = (t >> 5) - 1;
+ TEST_LOOKBEHIND (m_pos, out);
+ NEED_OP (t + 3 - 1);
+ goto copy_match;
+
+ }
+ else if (t >= 32)
+ {
+ t &= 31;
+ if (t == 0)
+ {
+ NEED_IP (1);
+ while (*ip == 0)
+ {
+ t += 255;
+ ip++;
+ NEED_IP (1);
+ }
+ t += 31 + *ip++;
+ }
+
+ m_pos = op - 1;
+ m_pos -= (ip[0] >> 2) + (ip[1] << 6);
+
+ ip += 2;
+ }
+ else if (t >= 16)
+ {
+ m_pos = op;
+ m_pos -= (t & 8) << 11;
+
+ t &= 7;
+ if (t == 0)
+ {
+ NEED_IP (1);
+ while (*ip == 0)
+ {
+ t += 255;
+ ip++;
+ NEED_IP (1);
+ }
+ t += 7 + *ip++;
+ }
+
+ m_pos -= (ip[0] >> 2) + (ip[1] << 6);
+
+ ip += 2;
+ if (m_pos == op)
+ goto eof_found;
+ m_pos -= 0x4000;
+ }
+ else
+ {
+
+ m_pos = op - 1;
+ m_pos -= t >> 2;
+ m_pos -= *ip++ << 2;
+ TEST_LOOKBEHIND (m_pos, out);
+ NEED_OP (2);
+ *op++ = *m_pos++;
+ *op++ = *m_pos;
+
+ goto match_done;
+ }
+
+ TEST_LOOKBEHIND (m_pos, out);
+ NEED_OP (t + 3 - 1);
+ if (t >= 2 * 4 - (3 - 1)
+ && PTR_ALIGNED2_4 (op, m_pos))
+ {
+ COPY4 (op, m_pos);
+ op += 4;
+ m_pos += 4;
+ t -= 4 - (3 - 1);
+ do
+ {
+ COPY4 (op, m_pos);
+ op += 4;
+ m_pos += 4;
+ t -= 4;
+ }
+ while (t >= 4);
+ if (t > 0)
+ do
+ *op++ = *m_pos++;
+ while (--t > 0);
+ }
+ else
+
+ {
+ copy_match:
+ *op++ = *m_pos++;
+ *op++ = *m_pos++;
+ do
+ *op++ = *m_pos++;
+ while (--t > 0);
+ }
+
+ match_done:
+ t = ip[-2] & 3;
+
+ if (t == 0)
+ break;
+
+ match_next:
+ NEED_OP (t);
+ NEED_IP (t + 1);
+ do
+ *op++ = *ip++;
+ while (--t > 0);
+ t = *ip++;
+ }
+ }
+ *out_len = op - out;
+ return LZO_E_EOF_NOT_FOUND;
+
+ eof_found:
+ *out_len = op - out;
+ return (ip == ip_end ? LZO_E_OK :
+ (ip <
+ ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN));
+
+ input_overrun:
+ *out_len = op - out;
+ return LZO_E_INPUT_OVERRUN;
+
+ output_overrun:
+ *out_len = op - out;
+ return LZO_E_OUTPUT_OVERRUN;
+
+ lookbehind_overrun:
+ *out_len = op - out;
+ return LZO_E_LOOKBEHIND_OVERRUN;
+}
+
+int lzo_decompress(unsigned char *data_in, unsigned char *cpage_out,
+ u32 srclen, u32 destlen)
+{
+ lzo_uint outlen = destlen;
+ return lzo1x_decompress (data_in, srclen, cpage_out, &outlen, NULL);
+}
diff --git a/roms/u-boot/fs/jffs2/compr_rtime.c b/roms/u-boot/fs/jffs2/compr_rtime.c
new file mode 100644
index 000000000..89b9f2f13
--- /dev/null
+++ b/roms/u-boot/fs/jffs2/compr_rtime.c
@@ -0,0 +1,87 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001 Red Hat, Inc.
+ *
+ * Created by Arjan van de Ven <arjanv@redhat.com>
+ *
+ * The original JFFS, from which the design for JFFS2 was derived,
+ * was designed and implemented by Axis Communications AB.
+ *
+ * The contents of this file are subject to the Red Hat eCos Public
+ * License Version 1.1 (the "Licence"); you may not use this file
+ * except in compliance with the Licence. You may obtain a copy of
+ * the Licence at http://www.redhat.com/
+ *
+ * Software distributed under the Licence is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing rights and
+ * limitations under the Licence.
+ *
+ * The Original Code is JFFS2 - Journalling Flash File System, version 2
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in
+ * which case the provisions of the GPL are applicable instead of the
+ * above. If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use your
+ * version of this file under the RHEPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice and
+ * other provisions required by the GPL. If you do not delete the
+ * provisions above, a recipient may use your version of this file
+ * under either the RHEPL or the GPL.
+ *
+ * $Id: compr_rtime.c,v 1.2 2002/01/24 22:58:42 rfeany Exp $
+ *
+ *
+ * Very simple lz77-ish encoder.
+ *
+ * Theory of operation: Both encoder and decoder have a list of "last
+ * occurances" for every possible source-value; after sending the
+ * first source-byte, the second byte indicated the "run" length of
+ * matches
+ *
+ * The algorithm is intended to only send "whole bytes", no bit-messing.
+ *
+ */
+
+#include <config.h>
+#include <jffs2/jffs2.h>
+
+void rtime_decompress(unsigned char *data_in, unsigned char *cpage_out,
+ u32 srclen, u32 destlen)
+{
+ int positions[256];
+ int outpos;
+ int pos;
+ int i;
+
+ outpos = pos = 0;
+
+ for (i = 0; i < 256; positions[i++] = 0);
+
+ while (outpos<destlen) {
+ unsigned char value;
+ int backoffs;
+ int repeat;
+
+ value = data_in[pos++];
+ cpage_out[outpos++] = value; /* first the verbatim copied byte */
+ repeat = data_in[pos++];
+ backoffs = positions[value];
+
+ positions[value]=outpos;
+ if (repeat) {
+ if (backoffs + repeat >= outpos) {
+ while(repeat) {
+ cpage_out[outpos++] = cpage_out[backoffs++];
+ repeat--;
+ }
+ } else {
+ for (i = 0; i < repeat; i++)
+ *(cpage_out + outpos + i) = *(cpage_out + backoffs + i);
+ outpos+=repeat;
+ }
+ }
+ }
+}
diff --git a/roms/u-boot/fs/jffs2/compr_rubin.c b/roms/u-boot/fs/jffs2/compr_rubin.c
new file mode 100644
index 000000000..9ff221773
--- /dev/null
+++ b/roms/u-boot/fs/jffs2/compr_rubin.c
@@ -0,0 +1,122 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001 Red Hat, Inc.
+ *
+ * Created by Arjan van de Ven <arjanv@redhat.com>
+ *
+ * Heavily modified by Russ Dill <Russ.Dill@asu.edu> in an attempt at
+ * a little more speed.
+ *
+ * The original JFFS, from which the design for JFFS2 was derived,
+ * was designed and implemented by Axis Communications AB.
+ *
+ * The contents of this file are subject to the Red Hat eCos Public
+ * License Version 1.1 (the "Licence"); you may not use this file
+ * except in compliance with the Licence. You may obtain a copy of
+ * the Licence at http://www.redhat.com/
+ *
+ * Software distributed under the Licence is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing rights and
+ * limitations under the Licence.
+ *
+ * The Original Code is JFFS2 - Journalling Flash File System, version 2
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in
+ * which case the provisions of the GPL are applicable instead of the
+ * above. If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use your
+ * version of this file under the RHEPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice and
+ * other provisions required by the GPL. If you do not delete the
+ * provisions above, a recipient may use your version of this file
+ * under either the RHEPL or the GPL.
+ *
+ * $Id: compr_rubin.c,v 1.2 2002/01/24 22:58:42 rfeany Exp $
+ *
+ */
+
+#include <config.h>
+#include <jffs2/jffs2.h>
+#include <jffs2/compr_rubin.h>
+
+
+void rubin_do_decompress(unsigned char *bits, unsigned char *in,
+ unsigned char *page_out, __u32 destlen)
+{
+ register char *curr = (char *)page_out;
+ char *end = (char *)(page_out + destlen);
+ register unsigned long temp;
+ register unsigned long result;
+ register unsigned long p;
+ register unsigned long q;
+ register unsigned long rec_q;
+ register unsigned long bit;
+ register long i0;
+ unsigned long i;
+
+ /* init_pushpull */
+ temp = *(u32 *) in;
+ bit = 16;
+
+ /* init_rubin */
+ q = 0;
+ p = (long) (2 * UPPER_BIT_RUBIN);
+
+ /* init_decode */
+ rec_q = (in[0] << 8) | in[1];
+
+ while (curr < end) {
+ /* in byte */
+
+ result = 0;
+ for (i = 0; i < 8; i++) {
+ /* decode */
+
+ while ((q & UPPER_BIT_RUBIN) || ((p + q) <= UPPER_BIT_RUBIN)) {
+ q &= ~UPPER_BIT_RUBIN;
+ q <<= 1;
+ p <<= 1;
+ rec_q &= ~UPPER_BIT_RUBIN;
+ rec_q <<= 1;
+ rec_q |= (temp >> (bit++ ^ 7)) & 1;
+ if (bit > 31) {
+ u32 *p = (u32 *)in;
+ bit = 0;
+ temp = *(++p);
+ in = (unsigned char *)p;
+ }
+ }
+ i0 = (bits[i] * p) >> 8;
+
+ if (i0 <= 0) i0 = 1;
+ /* if it fails, it fails, we have our crc
+ if (i0 >= p) i0 = p - 1; */
+
+ result >>= 1;
+ if (rec_q < q + i0) {
+ /* result |= 0x00; */
+ p = i0;
+ } else {
+ result |= 0x80;
+ p -= i0;
+ q += i0;
+ }
+ }
+ *(curr++) = result;
+ }
+}
+
+void dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out,
+ unsigned long sourcelen, unsigned long dstlen)
+{
+ unsigned char bits[8];
+ int c;
+
+ for (c=0; c<8; c++)
+ bits[c] = (256 - data_in[c]);
+
+ rubin_do_decompress(bits, data_in+8, cpage_out, dstlen);
+}
diff --git a/roms/u-boot/fs/jffs2/compr_zlib.c b/roms/u-boot/fs/jffs2/compr_zlib.c
new file mode 100644
index 000000000..d306b6dc4
--- /dev/null
+++ b/roms/u-boot/fs/jffs2/compr_zlib.c
@@ -0,0 +1,48 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ *
+ * The original JFFS, from which the design for JFFS2 was derived,
+ * was designed and implemented by Axis Communications AB.
+ *
+ * The contents of this file are subject to the Red Hat eCos Public
+ * License Version 1.1 (the "Licence"); you may not use this file
+ * except in compliance with the Licence. You may obtain a copy of
+ * the Licence at http://www.redhat.com/
+ *
+ * Software distributed under the Licence is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing rights and
+ * limitations under the Licence.
+ *
+ * The Original Code is JFFS2 - Journalling Flash File System, version 2
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in
+ * which case the provisions of the GPL are applicable instead of the
+ * above. If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use your
+ * version of this file under the RHEPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice and
+ * other provisions required by the GPL. If you do not delete the
+ * provisions above, a recipient may use your version of this file
+ * under either the RHEPL or the GPL.
+ *
+ * $Id: compr_zlib.c,v 1.2 2002/01/24 22:58:42 rfeany Exp $
+ *
+ */
+
+#include <common.h>
+#include <config.h>
+#include <jffs2/jffs2.h>
+#include <jffs2/mini_inflate.h>
+
+long zlib_decompress(unsigned char *data_in, unsigned char *cpage_out,
+ __u32 srclen, __u32 destlen)
+{
+ return (decompress_block(cpage_out, data_in + 2, (void *) ldr_memcpy));
+
+}
diff --git a/roms/u-boot/fs/jffs2/jffs2_1pass.c b/roms/u-boot/fs/jffs2/jffs2_1pass.c
new file mode 100644
index 000000000..1818e8121
--- /dev/null
+++ b/roms/u-boot/fs/jffs2/jffs2_1pass.c
@@ -0,0 +1,1986 @@
+/*
+-------------------------------------------------------------------------
+ * Filename: jffs2.c
+ * Version: $Id: jffs2_1pass.c,v 1.7 2002/01/25 01:56:47 nyet Exp $
+ * Copyright: Copyright (C) 2001, Russ Dill
+ * Author: Russ Dill <Russ.Dill@asu.edu>
+ * Description: Module to load kernel from jffs2
+ *-----------------------------------------------------------------------*/
+/*
+ * some portions of this code are taken from jffs2, and as such, the
+ * following copyright notice is included.
+ *
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ *
+ * The original JFFS, from which the design for JFFS2 was derived,
+ * was designed and implemented by Axis Communications AB.
+ *
+ * The contents of this file are subject to the Red Hat eCos Public
+ * License Version 1.1 (the "Licence"); you may not use this file
+ * except in compliance with the Licence. You may obtain a copy of
+ * the Licence at http://www.redhat.com/
+ *
+ * Software distributed under the Licence is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing rights and
+ * limitations under the Licence.
+ *
+ * The Original Code is JFFS2 - Journalling Flash File System, version 2
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in
+ * which case the provisions of the GPL are applicable instead of the
+ * above. If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use your
+ * version of this file under the RHEPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice and
+ * other provisions required by the GPL. If you do not delete the
+ * provisions above, a recipient may use your version of this file
+ * under either the RHEPL or the GPL.
+ *
+ * $Id: jffs2_1pass.c,v 1.7 2002/01/25 01:56:47 nyet Exp $
+ *
+ */
+
+/* Ok, so anyone who knows the jffs2 code will probably want to get a papar
+ * bag to throw up into before reading this code. I looked through the jffs2
+ * code, the caching scheme is very elegant. I tried to keep the version
+ * for a bootloader as small and simple as possible. Instead of worring about
+ * unneccesary data copies, node scans, etc, I just optimized for the known
+ * common case, a kernel, which looks like:
+ * (1) most pages are 4096 bytes
+ * (2) version numbers are somewhat sorted in acsending order
+ * (3) multiple compressed blocks making up one page is uncommon
+ *
+ * So I create a linked list of decending version numbers (insertions at the
+ * head), and then for each page, walk down the list, until a matching page
+ * with 4096 bytes is found, and then decompress the watching pages in
+ * reverse order.
+ *
+ */
+
+/*
+ * Adapted by Nye Liu <nyet@zumanetworks.com> and
+ * Rex Feany <rfeany@zumanetworks.com>
+ * on Jan/2002 for U-Boot.
+ *
+ * Clipped out all the non-1pass functions, cleaned up warnings,
+ * wrappers, etc. No major changes to the code.
+ * Please, he really means it when he said have a paper bag
+ * handy. We needed it ;).
+ *
+ */
+
+/*
+ * Bugfixing by Kai-Uwe Bloem <kai-uwe.bloem@auerswald.de>, (C) Mar/2003
+ *
+ * - overhaul of the memory management. Removed much of the "paper-bagging"
+ * in that part of the code, fixed several bugs, now frees memory when
+ * partition is changed.
+ * It's still ugly :-(
+ * - fixed a bug in jffs2_1pass_read_inode where the file length calculation
+ * was incorrect. Removed a bit of the paper-bagging as well.
+ * - removed double crc calculation for fragment headers in jffs2_private.h
+ * for speedup.
+ * - scan_empty rewritten in a more "standard" manner (non-paperbag, that is).
+ * - spinning wheel now spins depending on how much memory has been scanned
+ * - lots of small changes all over the place to "improve" readability.
+ * - implemented fragment sorting to ensure that the newest data is copied
+ * if there are multiple copies of fragments for a certain file offset.
+ *
+ * The fragment sorting feature must be enabled by CONFIG_SYS_JFFS2_SORT_FRAGMENTS.
+ * Sorting is done while adding fragments to the lists, which is more or less a
+ * bubble sort. This takes a lot of time, and is most probably not an issue if
+ * the boot filesystem is always mounted readonly.
+ *
+ * You should define it if the boot filesystem is mounted writable, and updates
+ * to the boot files are done by copying files to that filesystem.
+ *
+ *
+ * There's a big issue left: endianess is completely ignored in this code. Duh!
+ *
+ *
+ * You still should have paper bags at hand :-(. The code lacks more or less
+ * any comment, and is still arcane and difficult to read in places. As this
+ * might be incompatible with any new code from the jffs2 maintainers anyway,
+ * it should probably be dumped and replaced by something like jffs2reader!
+ */
+
+
+#include <common.h>
+#include <config.h>
+#include <flash.h>
+#include <malloc.h>
+#include <div64.h>
+#include <linux/compiler.h>
+#include <linux/stat.h>
+#include <linux/time.h>
+#include <u-boot/crc.h>
+#include <watchdog.h>
+#include <jffs2/jffs2.h>
+#include <jffs2/jffs2_1pass.h>
+#include <linux/compat.h>
+#include <linux/errno.h>
+
+#include "jffs2_private.h"
+
+
+#define NODE_CHUNK 1024 /* size of memory allocation chunk in b_nodes */
+#define SPIN_BLKSIZE 18 /* spin after having scanned 1<<BLKSIZE bytes */
+
+/* Debugging switches */
+#undef DEBUG_DIRENTS /* print directory entry list after scan */
+#undef DEBUG_FRAGMENTS /* print fragment list after scan */
+#undef DEBUG /* enable debugging messages */
+
+
+#ifdef DEBUG
+# define DEBUGF(fmt,args...) printf(fmt ,##args)
+#else
+# define DEBUGF(fmt,args...)
+#endif
+
+#include "summary.h"
+
+/* keeps pointer to currentlu processed partition */
+static struct part_info *current_part;
+
+#if (defined(CONFIG_JFFS2_NAND) && \
+ defined(CONFIG_CMD_NAND) )
+#include <nand.h>
+/*
+ * Support for jffs2 on top of NAND-flash
+ *
+ * NAND memory isn't mapped in processor's address space,
+ * so data should be fetched from flash before
+ * being processed. This is exactly what functions declared
+ * here do.
+ *
+ */
+
+#define NAND_PAGE_SIZE 512
+#define NAND_PAGE_SHIFT 9
+#define NAND_PAGE_MASK (~(NAND_PAGE_SIZE-1))
+
+#ifndef NAND_CACHE_PAGES
+#define NAND_CACHE_PAGES 16
+#endif
+#define NAND_CACHE_SIZE (NAND_CACHE_PAGES*NAND_PAGE_SIZE)
+
+static u8* nand_cache = NULL;
+static u32 nand_cache_off = (u32)-1;
+
+static int read_nand_cached(u32 off, u32 size, u_char *buf)
+{
+ struct mtdids *id = current_part->dev->id;
+ struct mtd_info *mtd;
+ u32 bytes_read = 0;
+ size_t retlen;
+ size_t toread;
+ int cpy_bytes;
+
+ mtd = get_nand_dev_by_index(id->num);
+ if (!mtd)
+ return -1;
+
+ while (bytes_read < size) {
+ retlen = NAND_CACHE_SIZE;
+ if( nand_cache_off + retlen > mtd->size )
+ retlen = mtd->size - nand_cache_off;
+
+ if ((off + bytes_read < nand_cache_off) ||
+ (off + bytes_read >= nand_cache_off + retlen)) {
+ nand_cache_off = (off + bytes_read) & NAND_PAGE_MASK;
+ if (!nand_cache) {
+ /* This memory never gets freed but 'cause
+ it's a bootloader, nobody cares */
+ nand_cache = malloc(NAND_CACHE_SIZE);
+ if (!nand_cache) {
+ printf("read_nand_cached: can't alloc cache size %d bytes\n",
+ NAND_CACHE_SIZE);
+ return -1;
+ }
+ }
+
+ toread = NAND_CACHE_SIZE;
+ if( nand_cache_off + toread > mtd->size )
+ toread = mtd->size - nand_cache_off;
+
+ retlen = toread;
+ if (nand_read(mtd, nand_cache_off,
+ &retlen, nand_cache) < 0 ||
+ retlen != toread) {
+ printf("read_nand_cached: error reading nand off %#x size %d bytes\n",
+ nand_cache_off, toread);
+ return -1;
+ }
+ }
+ cpy_bytes = nand_cache_off + retlen - (off + bytes_read);
+ if (cpy_bytes > size - bytes_read)
+ cpy_bytes = size - bytes_read;
+ memcpy(buf + bytes_read,
+ nand_cache + off + bytes_read - nand_cache_off,
+ cpy_bytes);
+ bytes_read += cpy_bytes;
+ }
+ return bytes_read;
+}
+
+static void *get_fl_mem_nand(u32 off, u32 size, void *ext_buf)
+{
+ u_char *buf = ext_buf ? (u_char*)ext_buf : (u_char*)malloc(size);
+
+ if (NULL == buf) {
+ printf("get_fl_mem_nand: can't alloc %d bytes\n", size);
+ return NULL;
+ }
+ if (read_nand_cached(off, size, buf) < 0) {
+ if (!ext_buf)
+ free(buf);
+ return NULL;
+ }
+
+ return buf;
+}
+
+static void *get_node_mem_nand(u32 off, void *ext_buf)
+{
+ struct jffs2_unknown_node node;
+ void *ret = NULL;
+
+ if (NULL == get_fl_mem_nand(off, sizeof(node), &node))
+ return NULL;
+
+ if (!(ret = get_fl_mem_nand(off, node.magic ==
+ JFFS2_MAGIC_BITMASK ? node.totlen : sizeof(node),
+ ext_buf))) {
+ printf("off = %#x magic %#x type %#x node.totlen = %d\n",
+ off, node.magic, node.nodetype, node.totlen);
+ }
+ return ret;
+}
+
+static void put_fl_mem_nand(void *buf)
+{
+ free(buf);
+}
+#endif
+
+#if defined(CONFIG_CMD_ONENAND)
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/onenand.h>
+#include <onenand_uboot.h>
+
+#define ONENAND_PAGE_SIZE 2048
+#define ONENAND_PAGE_SHIFT 11
+#define ONENAND_PAGE_MASK (~(ONENAND_PAGE_SIZE-1))
+
+#ifndef ONENAND_CACHE_PAGES
+#define ONENAND_CACHE_PAGES 4
+#endif
+#define ONENAND_CACHE_SIZE (ONENAND_CACHE_PAGES*ONENAND_PAGE_SIZE)
+
+static u8* onenand_cache;
+static u32 onenand_cache_off = (u32)-1;
+
+static int read_onenand_cached(u32 off, u32 size, u_char *buf)
+{
+ u32 bytes_read = 0;
+ size_t retlen;
+ size_t toread;
+ int cpy_bytes;
+
+ while (bytes_read < size) {
+ retlen = ONENAND_CACHE_SIZE;
+ if( onenand_cache_off + retlen > onenand_mtd.size )
+ retlen = onenand_mtd.size - onenand_cache_off;
+
+ if ((off + bytes_read < onenand_cache_off) ||
+ (off + bytes_read >= onenand_cache_off + retlen)) {
+ onenand_cache_off = (off + bytes_read) & ONENAND_PAGE_MASK;
+ if (!onenand_cache) {
+ /* This memory never gets freed but 'cause
+ it's a bootloader, nobody cares */
+ onenand_cache = malloc(ONENAND_CACHE_SIZE);
+ if (!onenand_cache) {
+ printf("read_onenand_cached: can't alloc cache size %d bytes\n",
+ ONENAND_CACHE_SIZE);
+ return -1;
+ }
+ }
+
+ toread = ONENAND_CACHE_SIZE;
+ if( onenand_cache_off + toread > onenand_mtd.size )
+ toread = onenand_mtd.size - onenand_cache_off;
+ retlen = toread;
+ if (onenand_read(&onenand_mtd, onenand_cache_off, retlen,
+ &retlen, onenand_cache) < 0 ||
+ retlen != toread) {
+ printf("read_onenand_cached: error reading nand off %#x size %d bytes\n",
+ onenand_cache_off, toread);
+ return -1;
+ }
+ }
+ cpy_bytes = onenand_cache_off + retlen - (off + bytes_read);
+ if (cpy_bytes > size - bytes_read)
+ cpy_bytes = size - bytes_read;
+ memcpy(buf + bytes_read,
+ onenand_cache + off + bytes_read - onenand_cache_off,
+ cpy_bytes);
+ bytes_read += cpy_bytes;
+ }
+ return bytes_read;
+}
+
+static void *get_fl_mem_onenand(u32 off, u32 size, void *ext_buf)
+{
+ u_char *buf = ext_buf ? (u_char *)ext_buf : (u_char *)malloc(size);
+
+ if (NULL == buf) {
+ printf("get_fl_mem_onenand: can't alloc %d bytes\n", size);
+ return NULL;
+ }
+ if (read_onenand_cached(off, size, buf) < 0) {
+ if (!ext_buf)
+ free(buf);
+ return NULL;
+ }
+
+ return buf;
+}
+
+static void *get_node_mem_onenand(u32 off, void *ext_buf)
+{
+ struct jffs2_unknown_node node;
+ void *ret = NULL;
+
+ if (NULL == get_fl_mem_onenand(off, sizeof(node), &node))
+ return NULL;
+
+ ret = get_fl_mem_onenand(off, node.magic ==
+ JFFS2_MAGIC_BITMASK ? node.totlen : sizeof(node),
+ ext_buf);
+ if (!ret) {
+ printf("off = %#x magic %#x type %#x node.totlen = %d\n",
+ off, node.magic, node.nodetype, node.totlen);
+ }
+ return ret;
+}
+
+
+static void put_fl_mem_onenand(void *buf)
+{
+ free(buf);
+}
+#endif
+
+
+#if defined(CONFIG_CMD_FLASH)
+/*
+ * Support for jffs2 on top of NOR-flash
+ *
+ * NOR flash memory is mapped in processor's address space,
+ * just return address.
+ */
+static inline void *get_fl_mem_nor(u32 off, u32 size, void *ext_buf)
+{
+ u32 addr = off;
+ struct mtdids *id = current_part->dev->id;
+
+ extern flash_info_t flash_info[];
+ flash_info_t *flash = &flash_info[id->num];
+
+ addr += flash->start[0];
+ if (ext_buf) {
+ memcpy(ext_buf, (void *)addr, size);
+ return ext_buf;
+ }
+ return (void*)addr;
+}
+
+static inline void *get_node_mem_nor(u32 off, void *ext_buf)
+{
+ struct jffs2_unknown_node *pNode;
+
+ /* pNode will point directly to flash - don't provide external buffer
+ and don't care about size */
+ pNode = get_fl_mem_nor(off, 0, NULL);
+ return (void *)get_fl_mem_nor(off, pNode->magic == JFFS2_MAGIC_BITMASK ?
+ pNode->totlen : sizeof(*pNode), ext_buf);
+}
+#endif
+
+
+/*
+ * Generic jffs2 raw memory and node read routines.
+ *
+ */
+static inline void *get_fl_mem(u32 off, u32 size, void *ext_buf)
+{
+ struct mtdids *id = current_part->dev->id;
+
+ switch(id->type) {
+#if defined(CONFIG_CMD_FLASH)
+ case MTD_DEV_TYPE_NOR:
+ return get_fl_mem_nor(off, size, ext_buf);
+ break;
+#endif
+#if defined(CONFIG_JFFS2_NAND) && defined(CONFIG_CMD_NAND)
+ case MTD_DEV_TYPE_NAND:
+ return get_fl_mem_nand(off, size, ext_buf);
+ break;
+#endif
+#if defined(CONFIG_CMD_ONENAND)
+ case MTD_DEV_TYPE_ONENAND:
+ return get_fl_mem_onenand(off, size, ext_buf);
+ break;
+#endif
+ default:
+ printf("get_fl_mem: unknown device type, " \
+ "using raw offset!\n");
+ }
+ return (void*)off;
+}
+
+static inline void *get_node_mem(u32 off, void *ext_buf)
+{
+ struct mtdids *id = current_part->dev->id;
+
+ switch(id->type) {
+#if defined(CONFIG_CMD_FLASH)
+ case MTD_DEV_TYPE_NOR:
+ return get_node_mem_nor(off, ext_buf);
+ break;
+#endif
+#if defined(CONFIG_JFFS2_NAND) && \
+ defined(CONFIG_CMD_NAND)
+ case MTD_DEV_TYPE_NAND:
+ return get_node_mem_nand(off, ext_buf);
+ break;
+#endif
+#if defined(CONFIG_CMD_ONENAND)
+ case MTD_DEV_TYPE_ONENAND:
+ return get_node_mem_onenand(off, ext_buf);
+ break;
+#endif
+ default:
+ printf("get_fl_mem: unknown device type, " \
+ "using raw offset!\n");
+ }
+ return (void*)off;
+}
+
+static inline void put_fl_mem(void *buf, void *ext_buf)
+{
+ struct mtdids *id = current_part->dev->id;
+
+ /* If buf is the same as ext_buf, it was provided by the caller -
+ we shouldn't free it then. */
+ if (buf == ext_buf)
+ return;
+ switch (id->type) {
+#if defined(CONFIG_JFFS2_NAND) && defined(CONFIG_CMD_NAND)
+ case MTD_DEV_TYPE_NAND:
+ return put_fl_mem_nand(buf);
+#endif
+#if defined(CONFIG_CMD_ONENAND)
+ case MTD_DEV_TYPE_ONENAND:
+ return put_fl_mem_onenand(buf);
+#endif
+ }
+}
+
+/* Compression names */
+static char *compr_names[] = {
+ "NONE",
+ "ZERO",
+ "RTIME",
+ "RUBINMIPS",
+ "COPY",
+ "DYNRUBIN",
+ "ZLIB",
+#if defined(CONFIG_JFFS2_LZO)
+ "LZO",
+#endif
+};
+
+/* Memory management */
+struct mem_block {
+ u32 index;
+ struct mem_block *next;
+ struct b_node nodes[NODE_CHUNK];
+};
+
+
+static void
+free_nodes(struct b_list *list)
+{
+ while (list->listMemBase != NULL) {
+ struct mem_block *next = list->listMemBase->next;
+ free( list->listMemBase );
+ list->listMemBase = next;
+ }
+}
+
+static struct b_node *
+add_node(struct b_list *list)
+{
+ u32 index = 0;
+ struct mem_block *memBase;
+ struct b_node *b;
+
+ memBase = list->listMemBase;
+ if (memBase != NULL)
+ index = memBase->index;
+#if 0
+ putLabeledWord("add_node: index = ", index);
+ putLabeledWord("add_node: memBase = ", list->listMemBase);
+#endif
+
+ if (memBase == NULL || index >= NODE_CHUNK) {
+ /* we need more space before we continue */
+ memBase = mmalloc(sizeof(struct mem_block));
+ if (memBase == NULL) {
+ putstr("add_node: malloc failed\n");
+ return NULL;
+ }
+ memBase->next = list->listMemBase;
+ index = 0;
+#if 0
+ putLabeledWord("add_node: alloced a new membase at ", *memBase);
+#endif
+
+ }
+ /* now we have room to add it. */
+ b = &memBase->nodes[index];
+ index ++;
+
+ memBase->index = index;
+ list->listMemBase = memBase;
+ list->listCount++;
+ return b;
+}
+
+static struct b_node *
+insert_node(struct b_list *list)
+{
+ struct b_node *new;
+
+ if (!(new = add_node(list))) {
+ putstr("add_node failed!\r\n");
+ return NULL;
+ }
+ new->next = NULL;
+
+ if (list->listTail != NULL)
+ list->listTail->next = new;
+ else
+ list->listHead = new;
+ list->listTail = new;
+
+ return new;
+}
+
+#ifdef CONFIG_SYS_JFFS2_SORT_FRAGMENTS
+/* Sort data entries with the latest version last, so that if there
+ * is overlapping data the latest version will be used.
+ */
+static int compare_inodes(struct b_node *new, struct b_node *old)
+{
+ return new->version > old->version;
+}
+
+/* Sort directory entries so all entries in the same directory
+ * with the same name are grouped together, with the latest version
+ * last. This makes it easy to eliminate all but the latest version
+ * by marking the previous version dead by setting the inode to 0.
+ */
+static int compare_dirents(struct b_node *new, struct b_node *old)
+{
+ /*
+ * Using NULL as the buffer for NOR flash prevents the entire node
+ * being read. This makes most comparisons much quicker as only one
+ * or two entries from the node will be used most of the time.
+ */
+ struct jffs2_raw_dirent *jNew = get_node_mem(new->offset, NULL);
+ struct jffs2_raw_dirent *jOld = get_node_mem(old->offset, NULL);
+ int cmp;
+ int ret;
+
+ if (jNew->pino != jOld->pino) {
+ /* ascending sort by pino */
+ ret = jNew->pino > jOld->pino;
+ } else if (jNew->nsize != jOld->nsize) {
+ /*
+ * pino is the same, so use ascending sort by nsize,
+ * so we don't do strncmp unless we really must.
+ */
+ ret = jNew->nsize > jOld->nsize;
+ } else {
+ /*
+ * length is also the same, so use ascending sort by name
+ */
+ cmp = strncmp((char *)jNew->name, (char *)jOld->name,
+ jNew->nsize);
+ if (cmp != 0) {
+ ret = cmp > 0;
+ } else {
+ /*
+ * we have duplicate names in this directory,
+ * so use ascending sort by version
+ */
+ ret = jNew->version > jOld->version;
+ }
+ }
+ put_fl_mem(jNew, NULL);
+ put_fl_mem(jOld, NULL);
+
+ return ret;
+}
+#endif
+
+void
+jffs2_free_cache(struct part_info *part)
+{
+ struct b_lists *pL;
+
+ if (part->jffs2_priv != NULL) {
+ pL = (struct b_lists *)part->jffs2_priv;
+ free_nodes(&pL->frag);
+ free_nodes(&pL->dir);
+ free(pL->readbuf);
+ free(pL);
+ }
+}
+
+static u32
+jffs_init_1pass_list(struct part_info *part)
+{
+ struct b_lists *pL;
+
+ jffs2_free_cache(part);
+
+ if (NULL != (part->jffs2_priv = malloc(sizeof(struct b_lists)))) {
+ pL = (struct b_lists *)part->jffs2_priv;
+
+ memset(pL, 0, sizeof(*pL));
+#ifdef CONFIG_SYS_JFFS2_SORT_FRAGMENTS
+ pL->dir.listCompare = compare_dirents;
+ pL->frag.listCompare = compare_inodes;
+#endif
+ }
+ return 0;
+}
+
+/* find the inode from the slashless name given a parent */
+static long
+jffs2_1pass_read_inode(struct b_lists *pL, u32 inode, char *dest)
+{
+ struct b_node *b;
+ struct jffs2_raw_inode *jNode;
+ u32 totalSize = 0;
+ u32 latestVersion = 0;
+ uchar *lDest;
+ uchar *src;
+ int i;
+ u32 counter = 0;
+
+ /* Find file size before loading any data, so fragments that
+ * start past the end of file can be ignored. A fragment
+ * that is partially in the file is loaded, so extra data may
+ * be loaded up to the next 4K boundary above the file size.
+ * This shouldn't cause trouble when loading kernel images, so
+ * we will live with it.
+ */
+ int latestOffset = -1;
+ for (b = pL->frag.listHead; b != NULL; b = b->next) {
+ if (inode == b->ino) {
+ /* get actual file length from the newest node */
+ if (b->version >= latestVersion) {
+ latestVersion = b->version;
+ latestOffset = b->offset;
+ }
+ }
+ }
+
+ if (latestOffset >= 0) {
+ jNode = (struct jffs2_raw_inode *)get_fl_mem(latestOffset,
+ sizeof(struct jffs2_raw_inode), pL->readbuf);
+ totalSize = jNode->isize;
+ put_fl_mem(jNode, pL->readbuf);
+ }
+
+ /*
+ * If no destination is provided, we are done.
+ * Just return the total size.
+ */
+ if (!dest)
+ return totalSize;
+
+ for (b = pL->frag.listHead; b != NULL; b = b->next) {
+ if (inode == b->ino) {
+ /*
+ * Copy just the node and not the data at this point,
+ * since we don't yet know if we need this data.
+ */
+ jNode = (struct jffs2_raw_inode *)get_fl_mem(b->offset,
+ sizeof(struct jffs2_raw_inode),
+ pL->readbuf);
+#if 0
+ putLabeledWord("\r\n\r\nread_inode: totlen = ", jNode->totlen);
+ putLabeledWord("read_inode: inode = ", jNode->ino);
+ putLabeledWord("read_inode: version = ", jNode->version);
+ putLabeledWord("read_inode: isize = ", jNode->isize);
+ putLabeledWord("read_inode: offset = ", jNode->offset);
+ putLabeledWord("read_inode: csize = ", jNode->csize);
+ putLabeledWord("read_inode: dsize = ", jNode->dsize);
+ putLabeledWord("read_inode: compr = ", jNode->compr);
+ putLabeledWord("read_inode: usercompr = ", jNode->usercompr);
+ putLabeledWord("read_inode: flags = ", jNode->flags);
+#endif
+
+ if(dest) {
+ /*
+ * Now that the inode has been checked,
+ * read the entire inode, including data.
+ */
+ put_fl_mem(jNode, pL->readbuf);
+ jNode = (struct jffs2_raw_inode *)
+ get_node_mem(b->offset, pL->readbuf);
+ src = ((uchar *)jNode) +
+ sizeof(struct jffs2_raw_inode);
+ /* ignore data behind latest known EOF */
+ if (jNode->offset > totalSize) {
+ put_fl_mem(jNode, pL->readbuf);
+ continue;
+ }
+ if (b->datacrc == CRC_UNKNOWN)
+ b->datacrc = data_crc(jNode) ?
+ CRC_OK : CRC_BAD;
+ if (b->datacrc == CRC_BAD) {
+ put_fl_mem(jNode, pL->readbuf);
+ continue;
+ }
+
+ lDest = (uchar *) (dest + jNode->offset);
+#if 0
+ putLabeledWord("read_inode: src = ", src);
+ putLabeledWord("read_inode: dest = ", lDest);
+#endif
+ switch (jNode->compr) {
+ case JFFS2_COMPR_NONE:
+ ldr_memcpy(lDest, src, jNode->dsize);
+ break;
+ case JFFS2_COMPR_ZERO:
+ for (i = 0; i < jNode->dsize; i++)
+ *(lDest++) = 0;
+ break;
+ case JFFS2_COMPR_RTIME:
+ rtime_decompress(src, lDest, jNode->csize, jNode->dsize);
+ break;
+ case JFFS2_COMPR_DYNRUBIN:
+ /* this is slow but it works */
+ dynrubin_decompress(src, lDest, jNode->csize, jNode->dsize);
+ break;
+ case JFFS2_COMPR_ZLIB:
+ zlib_decompress(src, lDest, jNode->csize, jNode->dsize);
+ break;
+#if defined(CONFIG_JFFS2_LZO)
+ case JFFS2_COMPR_LZO:
+ lzo_decompress(src, lDest, jNode->csize, jNode->dsize);
+ break;
+#endif
+ default:
+ /* unknown */
+ putLabeledWord("UNKNOWN COMPRESSION METHOD = ", jNode->compr);
+ put_fl_mem(jNode, pL->readbuf);
+ return -1;
+ break;
+ }
+ }
+
+#if 0
+ putLabeledWord("read_inode: totalSize = ", totalSize);
+#endif
+ put_fl_mem(jNode, pL->readbuf);
+ }
+ counter++;
+ }
+
+#if 0
+ putLabeledWord("read_inode: returning = ", totalSize);
+#endif
+ return totalSize;
+}
+
+/* find the inode from the slashless name given a parent */
+static u32
+jffs2_1pass_find_inode(struct b_lists * pL, const char *name, u32 pino)
+{
+ struct b_node *b;
+ struct jffs2_raw_dirent *jDir;
+ int len;
+ u32 counter;
+ u32 version = 0;
+ u32 inode = 0;
+
+ /* name is assumed slash free */
+ len = strlen(name);
+
+ counter = 0;
+ /* we need to search all and return the inode with the highest version */
+ for(b = pL->dir.listHead; b; b = b->next, counter++) {
+ jDir = (struct jffs2_raw_dirent *) get_node_mem(b->offset,
+ pL->readbuf);
+ if ((pino == jDir->pino) && (len == jDir->nsize) &&
+ (!strncmp((char *)jDir->name, name, len))) { /* a match */
+ if (jDir->version < version) {
+ put_fl_mem(jDir, pL->readbuf);
+ continue;
+ }
+
+ if (jDir->version == version && inode != 0) {
+ /* I'm pretty sure this isn't legal */
+ putstr(" ** ERROR ** ");
+ putnstr(jDir->name, jDir->nsize);
+ putLabeledWord(" has dup version =", version);
+ }
+ inode = jDir->ino;
+ version = jDir->version;
+ }
+#if 0
+ putstr("\r\nfind_inode:p&l ->");
+ putnstr(jDir->name, jDir->nsize);
+ putstr("\r\n");
+ putLabeledWord("pino = ", jDir->pino);
+ putLabeledWord("nsize = ", jDir->nsize);
+ putLabeledWord("b = ", (u32) b);
+ putLabeledWord("counter = ", counter);
+#endif
+ put_fl_mem(jDir, pL->readbuf);
+ }
+ return inode;
+}
+
+char *mkmodestr(unsigned long mode, char *str)
+{
+ static const char *l = "xwr";
+ int mask = 1, i;
+ char c;
+
+ switch (mode & S_IFMT) {
+ case S_IFDIR: str[0] = 'd'; break;
+ case S_IFBLK: str[0] = 'b'; break;
+ case S_IFCHR: str[0] = 'c'; break;
+ case S_IFIFO: str[0] = 'f'; break;
+ case S_IFLNK: str[0] = 'l'; break;
+ case S_IFSOCK: str[0] = 's'; break;
+ case S_IFREG: str[0] = '-'; break;
+ default: str[0] = '?';
+ }
+
+ for(i = 0; i < 9; i++) {
+ c = l[i%3];
+ str[9-i] = (mode & mask)?c:'-';
+ mask = mask<<1;
+ }
+
+ if(mode & S_ISUID) str[3] = (mode & S_IXUSR)?'s':'S';
+ if(mode & S_ISGID) str[6] = (mode & S_IXGRP)?'s':'S';
+ if(mode & S_ISVTX) str[9] = (mode & S_IXOTH)?'t':'T';
+ str[10] = '\0';
+ return str;
+}
+
+static inline void dump_stat(struct stat *st, const char *name)
+{
+ char str[20];
+ char s[64], *p;
+
+ if (st->st_mtime == (time_t)(-1)) /* some ctimes really hate -1 */
+ st->st_mtime = 1;
+
+ ctime_r((time_t *)&st->st_mtime, s/*,64*/); /* newlib ctime doesn't have buflen */
+
+ if ((p = strchr(s,'\n')) != NULL) *p = '\0';
+ if ((p = strchr(s,'\r')) != NULL) *p = '\0';
+
+/*
+ printf("%6lo %s %8ld %s %s\n", st->st_mode, mkmodestr(st->st_mode, str),
+ st->st_size, s, name);
+*/
+
+ printf(" %s %8ld %s %s", mkmodestr(st->st_mode,str), st->st_size, s, name);
+}
+
+static inline u32 dump_inode(struct b_lists * pL, struct jffs2_raw_dirent *d, struct jffs2_raw_inode *i)
+{
+ char fname[256];
+ struct stat st;
+
+ if(!d || !i) return -1;
+
+ strncpy(fname, (char *)d->name, d->nsize);
+ fname[d->nsize] = '\0';
+
+ memset(&st,0,sizeof(st));
+
+ st.st_mtime = i->mtime;
+ st.st_mode = i->mode;
+ st.st_ino = i->ino;
+ st.st_size = i->isize;
+
+ dump_stat(&st, fname);
+
+ if (d->type == DT_LNK) {
+ unsigned char *src = (unsigned char *) (&i[1]);
+ putstr(" -> ");
+ putnstr(src, (int)i->dsize);
+ }
+
+ putstr("\r\n");
+
+ return 0;
+}
+
+/* list inodes with the given pino */
+static u32
+jffs2_1pass_list_inodes(struct b_lists * pL, u32 pino)
+{
+ struct b_node *b;
+ struct jffs2_raw_dirent *jDir;
+
+ for (b = pL->dir.listHead; b; b = b->next) {
+ if (pino == b->pino) {
+ u32 i_version = 0;
+ int i_offset = -1;
+ struct jffs2_raw_inode *jNode = NULL;
+ struct b_node *b2;
+
+ jDir = (struct jffs2_raw_dirent *)
+ get_node_mem(b->offset, pL->readbuf);
+#ifdef CONFIG_SYS_JFFS2_SORT_FRAGMENTS
+ /* Check for more recent versions of this file */
+ int match;
+ do {
+ struct b_node *next = b->next;
+ struct jffs2_raw_dirent *jDirNext;
+ if (!next)
+ break;
+ jDirNext = (struct jffs2_raw_dirent *)
+ get_node_mem(next->offset, NULL);
+ match = jDirNext->pino == jDir->pino &&
+ jDirNext->nsize == jDir->nsize &&
+ strncmp((char *)jDirNext->name,
+ (char *)jDir->name,
+ jDir->nsize) == 0;
+ if (match) {
+ /* Use next. It is more recent */
+ b = next;
+ /* Update buffer with the new info */
+ *jDir = *jDirNext;
+ }
+ put_fl_mem(jDirNext, NULL);
+ } while (match);
+#endif
+ if (jDir->ino == 0) {
+ /* Deleted file */
+ put_fl_mem(jDir, pL->readbuf);
+ continue;
+ }
+
+ for (b2 = pL->frag.listHead; b2; b2 = b2->next) {
+ if (b2->ino == jDir->ino &&
+ b2->version >= i_version) {
+ i_version = b2->version;
+ i_offset = b2->offset;
+ }
+ }
+
+ if (i_version >= 0) {
+ if (jDir->type == DT_LNK)
+ jNode = get_node_mem(i_offset, NULL);
+ else
+ jNode = get_fl_mem(i_offset,
+ sizeof(*jNode),
+ NULL);
+ }
+
+ dump_inode(pL, jDir, jNode);
+ put_fl_mem(jNode, NULL);
+
+ put_fl_mem(jDir, pL->readbuf);
+ }
+ }
+ return pino;
+}
+
+static u32
+jffs2_1pass_search_inode(struct b_lists * pL, const char *fname, u32 pino)
+{
+ int i;
+ char tmp[256];
+ char working_tmp[256];
+ char *c;
+
+ /* discard any leading slash */
+ i = 0;
+ while (fname[i] == '/')
+ i++;
+ strcpy(tmp, &fname[i]);
+
+ while ((c = (char *) strchr(tmp, '/'))) /* we are still dired searching */
+ {
+ strncpy(working_tmp, tmp, c - tmp);
+ working_tmp[c - tmp] = '\0';
+#if 0
+ putstr("search_inode: tmp = ");
+ putstr(tmp);
+ putstr("\r\n");
+ putstr("search_inode: wtmp = ");
+ putstr(working_tmp);
+ putstr("\r\n");
+ putstr("search_inode: c = ");
+ putstr(c);
+ putstr("\r\n");
+#endif
+ for (i = 0; i < strlen(c) - 1; i++)
+ tmp[i] = c[i + 1];
+ tmp[i] = '\0';
+#if 0
+ putstr("search_inode: post tmp = ");
+ putstr(tmp);
+ putstr("\r\n");
+#endif
+
+ if (!(pino = jffs2_1pass_find_inode(pL, working_tmp, pino))) {
+ putstr("find_inode failed for name=");
+ putstr(working_tmp);
+ putstr("\r\n");
+ return 0;
+ }
+ }
+ /* this is for the bare filename, directories have already been mapped */
+ if (!(pino = jffs2_1pass_find_inode(pL, tmp, pino))) {
+ putstr("find_inode failed for name=");
+ putstr(tmp);
+ putstr("\r\n");
+ return 0;
+ }
+ return pino;
+
+}
+
+static u32
+jffs2_1pass_resolve_inode(struct b_lists * pL, u32 ino)
+{
+ struct b_node *b;
+ struct b_node *b2;
+ struct jffs2_raw_dirent *jDir;
+ struct jffs2_raw_inode *jNode;
+ u8 jDirFoundType = 0;
+ u32 jDirFoundIno = 0;
+ u32 jDirFoundPino = 0;
+ char tmp[256];
+ u32 version = 0;
+ u32 pino;
+ unsigned char *src;
+
+ /* we need to search all and return the inode with the highest version */
+ for(b = pL->dir.listHead; b; b = b->next) {
+ jDir = (struct jffs2_raw_dirent *) get_node_mem(b->offset,
+ pL->readbuf);
+ if (ino == jDir->ino) {
+ if (jDir->version < version) {
+ put_fl_mem(jDir, pL->readbuf);
+ continue;
+ }
+
+ if (jDir->version == version && jDirFoundType) {
+ /* I'm pretty sure this isn't legal */
+ putstr(" ** ERROR ** ");
+ putnstr(jDir->name, jDir->nsize);
+ putLabeledWord(" has dup version (resolve) = ",
+ version);
+ }
+
+ jDirFoundType = jDir->type;
+ jDirFoundIno = jDir->ino;
+ jDirFoundPino = jDir->pino;
+ version = jDir->version;
+ }
+ put_fl_mem(jDir, pL->readbuf);
+ }
+ /* now we found the right entry again. (shoulda returned inode*) */
+ if (jDirFoundType != DT_LNK)
+ return jDirFoundIno;
+
+ /* it's a soft link so we follow it again. */
+ b2 = pL->frag.listHead;
+ while (b2) {
+ jNode = (struct jffs2_raw_inode *) get_node_mem(b2->offset,
+ pL->readbuf);
+ if (jNode->ino == jDirFoundIno) {
+ src = (unsigned char *)jNode + sizeof(struct jffs2_raw_inode);
+
+#if 0
+ putLabeledWord("\t\t dsize = ", jNode->dsize);
+ putstr("\t\t target = ");
+ putnstr(src, jNode->dsize);
+ putstr("\r\n");
+#endif
+ strncpy(tmp, (char *)src, jNode->dsize);
+ tmp[jNode->dsize] = '\0';
+ put_fl_mem(jNode, pL->readbuf);
+ break;
+ }
+ b2 = b2->next;
+ put_fl_mem(jNode, pL->readbuf);
+ }
+ /* ok so the name of the new file to find is in tmp */
+ /* if it starts with a slash it is root based else shared dirs */
+ if (tmp[0] == '/')
+ pino = 1;
+ else
+ pino = jDirFoundPino;
+
+ return jffs2_1pass_search_inode(pL, tmp, pino);
+}
+
+static u32
+jffs2_1pass_search_list_inodes(struct b_lists * pL, const char *fname, u32 pino)
+{
+ int i;
+ char tmp[256];
+ char working_tmp[256];
+ char *c;
+
+ /* discard any leading slash */
+ i = 0;
+ while (fname[i] == '/')
+ i++;
+ strcpy(tmp, &fname[i]);
+ working_tmp[0] = '\0';
+ while ((c = (char *) strchr(tmp, '/'))) /* we are still dired searching */
+ {
+ strncpy(working_tmp, tmp, c - tmp);
+ working_tmp[c - tmp] = '\0';
+ for (i = 0; i < strlen(c) - 1; i++)
+ tmp[i] = c[i + 1];
+ tmp[i] = '\0';
+ /* only a failure if we arent looking at top level */
+ if (!(pino = jffs2_1pass_find_inode(pL, working_tmp, pino)) &&
+ (working_tmp[0])) {
+ putstr("find_inode failed for name=");
+ putstr(working_tmp);
+ putstr("\r\n");
+ return 0;
+ }
+ }
+
+ if (tmp[0] && !(pino = jffs2_1pass_find_inode(pL, tmp, pino))) {
+ putstr("find_inode failed for name=");
+ putstr(tmp);
+ putstr("\r\n");
+ return 0;
+ }
+ /* this is for the bare filename, directories have already been mapped */
+ if (!(pino = jffs2_1pass_list_inodes(pL, pino))) {
+ putstr("find_inode failed for name=");
+ putstr(tmp);
+ putstr("\r\n");
+ return 0;
+ }
+ return pino;
+
+}
+
+unsigned char
+jffs2_1pass_rescan_needed(struct part_info *part)
+{
+ struct b_node *b;
+ struct jffs2_unknown_node onode;
+ struct jffs2_unknown_node *node;
+ struct b_lists *pL = (struct b_lists *)part->jffs2_priv;
+
+ if (part->jffs2_priv == 0){
+ DEBUGF ("rescan: First time in use\n");
+ return 1;
+ }
+
+ /* if we have no list, we need to rescan */
+ if (pL->frag.listCount == 0) {
+ DEBUGF ("rescan: fraglist zero\n");
+ return 1;
+ }
+
+ /* but suppose someone reflashed a partition at the same offset... */
+ b = pL->dir.listHead;
+ while (b) {
+ node = (struct jffs2_unknown_node *) get_fl_mem(b->offset,
+ sizeof(onode), &onode);
+ if (node->nodetype != JFFS2_NODETYPE_DIRENT) {
+ DEBUGF ("rescan: fs changed beneath me? (%lx)\n",
+ (unsigned long) b->offset);
+ return 1;
+ }
+ b = b->next;
+ }
+ return 0;
+}
+
+#ifdef CONFIG_JFFS2_SUMMARY
+static u32 sum_get_unaligned32(u32 *ptr)
+{
+ u32 val;
+ u8 *p = (u8 *)ptr;
+
+ val = *p | (*(p + 1) << 8) | (*(p + 2) << 16) | (*(p + 3) << 24);
+
+ return __le32_to_cpu(val);
+}
+
+static u16 sum_get_unaligned16(u16 *ptr)
+{
+ u16 val;
+ u8 *p = (u8 *)ptr;
+
+ val = *p | (*(p + 1) << 8);
+
+ return __le16_to_cpu(val);
+}
+
+#define dbg_summary(...) do {} while (0);
+/*
+ * Process the stored summary information - helper function for
+ * jffs2_sum_scan_sumnode()
+ */
+
+static int jffs2_sum_process_sum_data(struct part_info *part, uint32_t offset,
+ struct jffs2_raw_summary *summary,
+ struct b_lists *pL)
+{
+ void *sp;
+ int i, pass;
+ struct b_node *b;
+
+ for (pass = 0; pass < 2; pass++) {
+ sp = summary->sum;
+
+ for (i = 0; i < summary->sum_num; i++) {
+ struct jffs2_sum_unknown_flash *spu = sp;
+ dbg_summary("processing summary index %d\n", i);
+
+ switch (sum_get_unaligned16(&spu->nodetype)) {
+ case JFFS2_NODETYPE_INODE: {
+ struct jffs2_sum_inode_flash *spi;
+ if (pass) {
+ spi = sp;
+
+ b = insert_node(&pL->frag);
+ if (!b)
+ return -1;
+ b->offset = (u32)part->offset +
+ offset +
+ sum_get_unaligned32(
+ &spi->offset);
+ b->version = sum_get_unaligned32(
+ &spi->version);
+ b->ino = sum_get_unaligned32(
+ &spi->inode);
+ b->datacrc = CRC_UNKNOWN;
+ }
+
+ sp += JFFS2_SUMMARY_INODE_SIZE;
+
+ break;
+ }
+ case JFFS2_NODETYPE_DIRENT: {
+ struct jffs2_sum_dirent_flash *spd;
+ spd = sp;
+ if (pass) {
+ b = insert_node(&pL->dir);
+ if (!b)
+ return -1;
+ b->offset = (u32)part->offset +
+ offset +
+ sum_get_unaligned32(
+ &spd->offset);
+ b->version = sum_get_unaligned32(
+ &spd->version);
+ b->pino = sum_get_unaligned32(
+ &spd->pino);
+ b->datacrc = CRC_UNKNOWN;
+ }
+
+ sp += JFFS2_SUMMARY_DIRENT_SIZE(
+ spd->nsize);
+
+ break;
+ }
+ default : {
+ uint16_t nodetype = sum_get_unaligned16(
+ &spu->nodetype);
+ printf("Unsupported node type %x found"
+ " in summary!\n",
+ nodetype);
+ if ((nodetype & JFFS2_COMPAT_MASK) ==
+ JFFS2_FEATURE_INCOMPAT)
+ return -EIO;
+ return -EBADMSG;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/* Process the summary node - called from jffs2_scan_eraseblock() */
+int jffs2_sum_scan_sumnode(struct part_info *part, uint32_t offset,
+ struct jffs2_raw_summary *summary, uint32_t sumsize,
+ struct b_lists *pL)
+{
+ struct jffs2_unknown_node crcnode;
+ int ret, __maybe_unused ofs;
+ uint32_t crc;
+
+ ofs = part->sector_size - sumsize;
+
+ dbg_summary("summary found for 0x%08x at 0x%08x (0x%x bytes)\n",
+ offset, offset + ofs, sumsize);
+
+ /* OK, now check for node validity and CRC */
+ crcnode.magic = JFFS2_MAGIC_BITMASK;
+ crcnode.nodetype = JFFS2_NODETYPE_SUMMARY;
+ crcnode.totlen = summary->totlen;
+ crc = crc32_no_comp(0, (uchar *)&crcnode, sizeof(crcnode)-4);
+
+ if (summary->hdr_crc != crc) {
+ dbg_summary("Summary node header is corrupt (bad CRC or "
+ "no summary at all)\n");
+ goto crc_err;
+ }
+
+ if (summary->totlen != sumsize) {
+ dbg_summary("Summary node is corrupt (wrong erasesize?)\n");
+ goto crc_err;
+ }
+
+ crc = crc32_no_comp(0, (uchar *)summary,
+ sizeof(struct jffs2_raw_summary)-8);
+
+ if (summary->node_crc != crc) {
+ dbg_summary("Summary node is corrupt (bad CRC)\n");
+ goto crc_err;
+ }
+
+ crc = crc32_no_comp(0, (uchar *)summary->sum,
+ sumsize - sizeof(struct jffs2_raw_summary));
+
+ if (summary->sum_crc != crc) {
+ dbg_summary("Summary node data is corrupt (bad CRC)\n");
+ goto crc_err;
+ }
+
+ if (summary->cln_mkr)
+ dbg_summary("Summary : CLEANMARKER node \n");
+
+ ret = jffs2_sum_process_sum_data(part, offset, summary, pL);
+ if (ret == -EBADMSG)
+ return 0;
+ if (ret)
+ return ret; /* real error */
+
+ return 1;
+
+crc_err:
+ putstr("Summary node crc error, skipping summary information.\n");
+
+ return 0;
+}
+#endif /* CONFIG_JFFS2_SUMMARY */
+
+#ifdef DEBUG_FRAGMENTS
+static void
+dump_fragments(struct b_lists *pL)
+{
+ struct b_node *b;
+ struct jffs2_raw_inode ojNode;
+ struct jffs2_raw_inode *jNode;
+
+ putstr("\r\n\r\n******The fragment Entries******\r\n");
+ b = pL->frag.listHead;
+ while (b) {
+ jNode = (struct jffs2_raw_inode *) get_fl_mem(b->offset,
+ sizeof(ojNode), &ojNode);
+ putLabeledWord("\r\n\tbuild_list: FLASH_OFFSET = ", b->offset);
+ putLabeledWord("\tbuild_list: totlen = ", jNode->totlen);
+ putLabeledWord("\tbuild_list: inode = ", jNode->ino);
+ putLabeledWord("\tbuild_list: version = ", jNode->version);
+ putLabeledWord("\tbuild_list: isize = ", jNode->isize);
+ putLabeledWord("\tbuild_list: atime = ", jNode->atime);
+ putLabeledWord("\tbuild_list: offset = ", jNode->offset);
+ putLabeledWord("\tbuild_list: csize = ", jNode->csize);
+ putLabeledWord("\tbuild_list: dsize = ", jNode->dsize);
+ putLabeledWord("\tbuild_list: compr = ", jNode->compr);
+ putLabeledWord("\tbuild_list: usercompr = ", jNode->usercompr);
+ putLabeledWord("\tbuild_list: flags = ", jNode->flags);
+ putLabeledWord("\tbuild_list: offset = ", b->offset); /* FIXME: ? [RS] */
+ b = b->next;
+ }
+}
+#endif
+
+#ifdef DEBUG_DIRENTS
+static void
+dump_dirents(struct b_lists *pL)
+{
+ struct b_node *b;
+ struct jffs2_raw_dirent *jDir;
+
+ putstr("\r\n\r\n******The directory Entries******\r\n");
+ b = pL->dir.listHead;
+ while (b) {
+ jDir = (struct jffs2_raw_dirent *) get_node_mem(b->offset,
+ pL->readbuf);
+ putstr("\r\n");
+ putnstr(jDir->name, jDir->nsize);
+ putLabeledWord("\r\n\tbuild_list: magic = ", jDir->magic);
+ putLabeledWord("\tbuild_list: nodetype = ", jDir->nodetype);
+ putLabeledWord("\tbuild_list: hdr_crc = ", jDir->hdr_crc);
+ putLabeledWord("\tbuild_list: pino = ", jDir->pino);
+ putLabeledWord("\tbuild_list: version = ", jDir->version);
+ putLabeledWord("\tbuild_list: ino = ", jDir->ino);
+ putLabeledWord("\tbuild_list: mctime = ", jDir->mctime);
+ putLabeledWord("\tbuild_list: nsize = ", jDir->nsize);
+ putLabeledWord("\tbuild_list: type = ", jDir->type);
+ putLabeledWord("\tbuild_list: node_crc = ", jDir->node_crc);
+ putLabeledWord("\tbuild_list: name_crc = ", jDir->name_crc);
+ putLabeledWord("\tbuild_list: offset = ", b->offset); /* FIXME: ? [RS] */
+ b = b->next;
+ put_fl_mem(jDir, pL->readbuf);
+ }
+}
+#endif
+
+#define DEFAULT_EMPTY_SCAN_SIZE 256
+
+static inline uint32_t EMPTY_SCAN_SIZE(uint32_t sector_size)
+{
+ if (sector_size < DEFAULT_EMPTY_SCAN_SIZE)
+ return sector_size;
+ else
+ return DEFAULT_EMPTY_SCAN_SIZE;
+}
+
+static u32
+jffs2_1pass_build_lists(struct part_info * part)
+{
+ struct b_lists *pL;
+ union jffs2_node_union *node;
+ u32 nr_sectors;
+ u32 i;
+ u32 counter4 = 0;
+ u32 counterF = 0;
+ u32 counterN = 0;
+ u32 max_totlen = 0;
+ u32 buf_size;
+ char *buf;
+
+ nr_sectors = lldiv(part->size, part->sector_size);
+ /* turn off the lcd. Refreshing the lcd adds 50% overhead to the */
+ /* jffs2 list building enterprise nope. in newer versions the overhead is */
+ /* only about 5 %. not enough to inconvenience people for. */
+ /* lcd_off(); */
+
+ /* if we are building a list we need to refresh the cache. */
+ jffs_init_1pass_list(part);
+ pL = (struct b_lists *)part->jffs2_priv;
+ buf = malloc(DEFAULT_EMPTY_SCAN_SIZE);
+ puts ("Scanning JFFS2 FS: ");
+
+ /* start at the beginning of the partition */
+ for (i = 0; i < nr_sectors; i++) {
+ uint32_t sector_ofs = i * part->sector_size;
+ uint32_t buf_ofs = sector_ofs;
+ uint32_t buf_len;
+ uint32_t ofs, prevofs;
+#ifdef CONFIG_JFFS2_SUMMARY
+ struct jffs2_sum_marker *sm;
+ void *sumptr = NULL;
+ uint32_t sumlen;
+ int ret;
+#endif
+ /* Indicates a sector with a CLEANMARKER was found */
+ int clean_sector = 0;
+ struct jffs2_unknown_node crcnode;
+ struct b_node *b;
+
+ /* Set buf_size to maximum length */
+ buf_size = DEFAULT_EMPTY_SCAN_SIZE;
+ WATCHDOG_RESET();
+
+#ifdef CONFIG_JFFS2_SUMMARY
+ buf_len = sizeof(*sm);
+
+ /* Read as much as we want into the _end_ of the preallocated
+ * buffer
+ */
+ get_fl_mem(part->offset + sector_ofs + part->sector_size -
+ buf_len, buf_len, buf + buf_size - buf_len);
+
+ sm = (void *)buf + buf_size - sizeof(*sm);
+ if (sm->magic == JFFS2_SUM_MAGIC) {
+ sumlen = part->sector_size - sm->offset;
+ sumptr = buf + buf_size - sumlen;
+
+ /* Now, make sure the summary itself is available */
+ if (sumlen > buf_size) {
+ /* Need to kmalloc for this. */
+ sumptr = malloc(sumlen);
+ if (!sumptr) {
+ putstr("Can't get memory for summary "
+ "node!\n");
+ free(buf);
+ jffs2_free_cache(part);
+ return 0;
+ }
+ memcpy(sumptr + sumlen - buf_len, buf +
+ buf_size - buf_len, buf_len);
+ }
+ if (buf_len < sumlen) {
+ /* Need to read more so that the entire summary
+ * node is present
+ */
+ get_fl_mem(part->offset + sector_ofs +
+ part->sector_size - sumlen,
+ sumlen - buf_len, sumptr);
+ }
+ }
+
+ if (sumptr) {
+ ret = jffs2_sum_scan_sumnode(part, sector_ofs, sumptr,
+ sumlen, pL);
+
+ if (buf_size && sumlen > buf_size)
+ free(sumptr);
+ if (ret < 0) {
+ free(buf);
+ jffs2_free_cache(part);
+ return 0;
+ }
+ if (ret)
+ continue;
+
+ }
+#endif /* CONFIG_JFFS2_SUMMARY */
+
+ buf_len = EMPTY_SCAN_SIZE(part->sector_size);
+
+ get_fl_mem((u32)part->offset + buf_ofs, buf_len, buf);
+
+ /* We temporarily use 'ofs' as a pointer into the buffer/jeb */
+ ofs = 0;
+
+ /* Scan only 4KiB of 0xFF before declaring it's empty */
+ while (ofs < EMPTY_SCAN_SIZE(part->sector_size) &&
+ *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF)
+ ofs += 4;
+
+ if (ofs == EMPTY_SCAN_SIZE(part->sector_size))
+ continue;
+
+ ofs += sector_ofs;
+ prevofs = ofs - 1;
+ /*
+ * Set buf_size down to the minimum size required.
+ * This prevents reading in chunks of flash data unnecessarily.
+ */
+ buf_size = sizeof(union jffs2_node_union);
+
+ scan_more:
+ while (ofs < sector_ofs + part->sector_size) {
+ if (ofs == prevofs) {
+ printf("offset %08x already seen, skip\n", ofs);
+ ofs += 4;
+ counter4++;
+ continue;
+ }
+ prevofs = ofs;
+ if (sector_ofs + part->sector_size <
+ ofs + sizeof(struct jffs2_unknown_node))
+ break;
+ if (buf_ofs + buf_len <
+ ofs + sizeof(struct jffs2_unknown_node)) {
+ buf_len = min_t(uint32_t, buf_size, sector_ofs
+ + part->sector_size - ofs);
+ get_fl_mem((u32)part->offset + ofs, buf_len,
+ buf);
+ buf_ofs = ofs;
+ }
+
+ node = (union jffs2_node_union *)&buf[ofs - buf_ofs];
+
+ if (*(uint32_t *)(&buf[ofs-buf_ofs]) == 0xffffffff) {
+ uint32_t inbuf_ofs;
+ uint32_t scan_end;
+
+ ofs += 4;
+ scan_end = min_t(uint32_t, EMPTY_SCAN_SIZE(
+ part->sector_size)/8,
+ buf_len);
+ more_empty:
+ inbuf_ofs = ofs - buf_ofs;
+ while (inbuf_ofs < scan_end) {
+ if (*(uint32_t *)(&buf[inbuf_ofs]) !=
+ 0xffffffff)
+ goto scan_more;
+
+ inbuf_ofs += 4;
+ ofs += 4;
+ }
+ /* Ran off end. */
+ /*
+ * If this sector had a clean marker at the
+ * beginning, and immediately following this
+ * have been a bunch of FF bytes, treat the
+ * entire sector as empty.
+ */
+ if (clean_sector)
+ break;
+
+ /* See how much more there is to read in this
+ * eraseblock...
+ */
+ buf_len = min_t(uint32_t, buf_size,
+ sector_ofs +
+ part->sector_size - ofs);
+ if (!buf_len) {
+ /* No more to read. Break out of main
+ * loop without marking this range of
+ * empty space as dirty (because it's
+ * not)
+ */
+ break;
+ }
+ scan_end = buf_len;
+ get_fl_mem((u32)part->offset + ofs, buf_len,
+ buf);
+ buf_ofs = ofs;
+ goto more_empty;
+ }
+ /*
+ * Found something not erased in the sector, so reset
+ * the 'clean_sector' flag.
+ */
+ clean_sector = 0;
+ if (node->u.magic != JFFS2_MAGIC_BITMASK) {
+ ofs += 4;
+ counter4++;
+ continue;
+ }
+
+ crcnode.magic = node->u.magic;
+ crcnode.nodetype = node->u.nodetype | JFFS2_NODE_ACCURATE;
+ crcnode.totlen = node->u.totlen;
+ crcnode.hdr_crc = node->u.hdr_crc;
+ if (!hdr_crc(&crcnode)) {
+ ofs += 4;
+ counter4++;
+ continue;
+ }
+
+ if (ofs + node->u.totlen > sector_ofs + part->sector_size) {
+ ofs += 4;
+ counter4++;
+ continue;
+ }
+
+ if (!(node->u.nodetype & JFFS2_NODE_ACCURATE)) {
+ DEBUGF("Obsolete node type: %x len %d offset 0x%x\n",
+ node->u.nodetype, node->u.totlen, ofs);
+ ofs += ((node->u.totlen + 3) & ~3);
+ counterF++;
+ continue;
+ }
+
+ /* if its a fragment add it */
+ switch (node->u.nodetype) {
+ case JFFS2_NODETYPE_INODE:
+ if (buf_ofs + buf_len <
+ ofs + sizeof(struct jffs2_raw_inode)) {
+ buf_len = min_t(uint32_t,
+ sizeof(struct jffs2_raw_inode),
+ sector_ofs +
+ part->sector_size -
+ ofs);
+ get_fl_mem((u32)part->offset + ofs,
+ buf_len, buf);
+ buf_ofs = ofs;
+ node = (void *)buf;
+ }
+ if (!inode_crc((struct jffs2_raw_inode *)node))
+ break;
+
+ b = insert_node(&pL->frag);
+ if (!b) {
+ free(buf);
+ jffs2_free_cache(part);
+ return 0;
+ }
+ b->offset = (u32)part->offset + ofs;
+ b->version = node->i.version;
+ b->ino = node->i.ino;
+ if (max_totlen < node->u.totlen)
+ max_totlen = node->u.totlen;
+ break;
+ case JFFS2_NODETYPE_DIRENT:
+ if (buf_ofs + buf_len < ofs + sizeof(struct
+ jffs2_raw_dirent) +
+ ((struct
+ jffs2_raw_dirent *)
+ node)->nsize) {
+ buf_len = min_t(uint32_t,
+ node->u.totlen,
+ sector_ofs +
+ part->sector_size -
+ ofs);
+ get_fl_mem((u32)part->offset + ofs,
+ buf_len, buf);
+ buf_ofs = ofs;
+ node = (void *)buf;
+ }
+
+ if (!dirent_crc((struct jffs2_raw_dirent *)
+ node) ||
+ !dirent_name_crc(
+ (struct
+ jffs2_raw_dirent *)
+ node))
+ break;
+ if (! (counterN%100))
+ puts ("\b\b. ");
+ b = insert_node(&pL->dir);
+ if (!b) {
+ free(buf);
+ jffs2_free_cache(part);
+ return 0;
+ }
+ b->offset = (u32)part->offset + ofs;
+ b->version = node->d.version;
+ b->pino = node->d.pino;
+ if (max_totlen < node->u.totlen)
+ max_totlen = node->u.totlen;
+ counterN++;
+ break;
+ case JFFS2_NODETYPE_CLEANMARKER:
+ if (node->u.totlen != sizeof(struct jffs2_unknown_node))
+ printf("OOPS Cleanmarker has bad size "
+ "%d != %zu\n",
+ node->u.totlen,
+ sizeof(struct jffs2_unknown_node));
+ if (node->u.totlen ==
+ sizeof(struct jffs2_unknown_node) &&
+ ofs == sector_ofs) {
+ /*
+ * Found a CLEANMARKER at the beginning
+ * of the sector. It's in the correct
+ * place with correct size and CRC.
+ */
+ clean_sector = 1;
+ }
+ break;
+ case JFFS2_NODETYPE_PADDING:
+ if (node->u.totlen <
+ sizeof(struct jffs2_unknown_node))
+ printf("OOPS Padding has bad size "
+ "%d < %zu\n",
+ node->u.totlen,
+ sizeof(struct jffs2_unknown_node));
+ break;
+ case JFFS2_NODETYPE_SUMMARY:
+ break;
+ default:
+ printf("Unknown node type: %x len %d offset 0x%x\n",
+ node->u.nodetype,
+ node->u.totlen, ofs);
+ }
+ ofs += ((node->u.totlen + 3) & ~3);
+ counterF++;
+ }
+ }
+
+ free(buf);
+#if defined(CONFIG_SYS_JFFS2_SORT_FRAGMENTS)
+ /*
+ * Sort the lists.
+ */
+ sort_list(&pL->frag);
+ sort_list(&pL->dir);
+#endif
+ putstr("\b\b done.\r\n"); /* close off the dots */
+
+ /* We don't care if malloc failed - then each read operation will
+ * allocate its own buffer as necessary (NAND) or will read directly
+ * from flash (NOR).
+ */
+ pL->readbuf = malloc(max_totlen);
+
+ /* turn the lcd back on. */
+ /* splash(); */
+
+#if 0
+ putLabeledWord("dir entries = ", pL->dir.listCount);
+ putLabeledWord("frag entries = ", pL->frag.listCount);
+ putLabeledWord("+4 increments = ", counter4);
+ putLabeledWord("+file_offset increments = ", counterF);
+
+#endif
+
+#ifdef DEBUG_DIRENTS
+ dump_dirents(pL);
+#endif
+
+#ifdef DEBUG_FRAGMENTS
+ dump_fragments(pL);
+#endif
+
+ /* give visual feedback that we are done scanning the flash */
+ led_blink(0x0, 0x0, 0x1, 0x1); /* off, forever, on 100ms, off 100ms */
+ return 1;
+}
+
+
+static u32
+jffs2_1pass_fill_info(struct b_lists * pL, struct b_jffs2_info * piL)
+{
+ struct b_node *b;
+ struct jffs2_raw_inode ojNode;
+ struct jffs2_raw_inode *jNode;
+ int i;
+
+ for (i = 0; i < JFFS2_NUM_COMPR; i++) {
+ piL->compr_info[i].num_frags = 0;
+ piL->compr_info[i].compr_sum = 0;
+ piL->compr_info[i].decompr_sum = 0;
+ }
+
+ b = pL->frag.listHead;
+ while (b) {
+ jNode = (struct jffs2_raw_inode *) get_fl_mem(b->offset,
+ sizeof(ojNode), &ojNode);
+ if (jNode->compr < JFFS2_NUM_COMPR) {
+ piL->compr_info[jNode->compr].num_frags++;
+ piL->compr_info[jNode->compr].compr_sum += jNode->csize;
+ piL->compr_info[jNode->compr].decompr_sum += jNode->dsize;
+ }
+ b = b->next;
+ }
+ return 0;
+}
+
+
+static struct b_lists *
+jffs2_get_list(struct part_info * part, const char *who)
+{
+ /* copy requested part_info struct pointer to global location */
+ current_part = part;
+
+ if (jffs2_1pass_rescan_needed(part)) {
+ if (!jffs2_1pass_build_lists(part)) {
+ printf("%s: Failed to scan JFFSv2 file structure\n", who);
+ return NULL;
+ }
+ }
+ return (struct b_lists *)part->jffs2_priv;
+}
+
+
+/* Print directory / file contents */
+u32
+jffs2_1pass_ls(struct part_info * part, const char *fname)
+{
+ struct b_lists *pl;
+ long ret = 1;
+ u32 inode;
+
+ if (! (pl = jffs2_get_list(part, "ls")))
+ return 0;
+
+ if (! (inode = jffs2_1pass_search_list_inodes(pl, fname, 1))) {
+ putstr("ls: Failed to scan jffs2 file structure\r\n");
+ return 0;
+ }
+
+
+#if 0
+ putLabeledWord("found file at inode = ", inode);
+ putLabeledWord("read_inode returns = ", ret);
+#endif
+
+ return ret;
+}
+
+
+/* Load a file from flash into memory. fname can be a full path */
+u32
+jffs2_1pass_load(char *dest, struct part_info * part, const char *fname)
+{
+
+ struct b_lists *pl;
+ long ret = 1;
+ u32 inode;
+
+ if (! (pl = jffs2_get_list(part, "load")))
+ return 0;
+
+ if (! (inode = jffs2_1pass_search_inode(pl, fname, 1))) {
+ putstr("load: Failed to find inode\r\n");
+ return 0;
+ }
+
+ /* Resolve symlinks */
+ if (! (inode = jffs2_1pass_resolve_inode(pl, inode))) {
+ putstr("load: Failed to resolve inode structure\r\n");
+ return 0;
+ }
+
+ if ((ret = jffs2_1pass_read_inode(pl, inode, dest)) < 0) {
+ putstr("load: Failed to read inode\r\n");
+ return 0;
+ }
+
+ DEBUGF ("load: loaded '%s' to 0x%lx (%ld bytes)\n", fname,
+ (unsigned long) dest, ret);
+ return ret;
+}
+
+/* Return information about the fs on this partition */
+u32
+jffs2_1pass_info(struct part_info * part)
+{
+ struct b_jffs2_info info;
+ struct b_lists *pl;
+ int i;
+
+ if (! (pl = jffs2_get_list(part, "info")))
+ return 0;
+
+ jffs2_1pass_fill_info(pl, &info);
+ for (i = 0; i < JFFS2_NUM_COMPR; i++) {
+ printf ("Compression: %s\n"
+ "\tfrag count: %d\n"
+ "\tcompressed sum: %d\n"
+ "\tuncompressed sum: %d\n",
+ compr_names[i],
+ info.compr_info[i].num_frags,
+ info.compr_info[i].compr_sum,
+ info.compr_info[i].decompr_sum);
+ }
+ return 1;
+}
diff --git a/roms/u-boot/fs/jffs2/jffs2_private.h b/roms/u-boot/fs/jffs2/jffs2_private.h
new file mode 100644
index 000000000..65d19a76f
--- /dev/null
+++ b/roms/u-boot/fs/jffs2/jffs2_private.h
@@ -0,0 +1,110 @@
+#ifndef jffs2_private_h
+#define jffs2_private_h
+
+#include <jffs2/jffs2.h>
+
+
+struct b_node {
+ u32 offset;
+ struct b_node *next;
+ enum { CRC_UNKNOWN = 0, CRC_OK, CRC_BAD } datacrc;
+ u32 version;
+ union {
+ u32 ino; /* for inodes */
+ u32 pino; /* for dirents */
+ };
+};
+
+struct b_list {
+ struct b_node *listTail;
+ struct b_node *listHead;
+#ifdef CONFIG_SYS_JFFS2_SORT_FRAGMENTS
+ struct b_node *listLast;
+ int (*listCompare)(struct b_node *new, struct b_node *node);
+ u32 listLoops;
+#endif
+ u32 listCount;
+ struct mem_block *listMemBase;
+};
+
+struct b_lists {
+ struct b_list dir;
+ struct b_list frag;
+ void *readbuf;
+};
+
+struct b_compr_info {
+ u32 num_frags;
+ u32 compr_sum;
+ u32 decompr_sum;
+};
+
+struct b_jffs2_info {
+ struct b_compr_info compr_info[JFFS2_NUM_COMPR];
+};
+
+static inline int
+hdr_crc(struct jffs2_unknown_node *node)
+{
+#if 1
+ u32 crc = crc32_no_comp(0, (unsigned char *)node, sizeof(struct jffs2_unknown_node) - 4);
+#else
+ /* what's the semantics of this? why is this here? */
+ u32 crc = crc32_no_comp(~0, (unsigned char *)node, sizeof(struct jffs2_unknown_node) - 4);
+
+ crc ^= ~0;
+#endif
+ if (node->hdr_crc != crc) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+static inline int
+dirent_crc(struct jffs2_raw_dirent *node)
+{
+ if (node->node_crc != crc32_no_comp(0, (unsigned char *)node, sizeof(struct jffs2_raw_dirent) - 8)) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+static inline int
+dirent_name_crc(struct jffs2_raw_dirent *node)
+{
+ if (node->name_crc != crc32_no_comp(0, (unsigned char *)&(node->name), node->nsize)) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+static inline int
+inode_crc(struct jffs2_raw_inode *node)
+{
+ if (node->node_crc != crc32_no_comp(0, (unsigned char *)node, sizeof(struct jffs2_raw_inode) - 8)) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+static inline int
+data_crc(struct jffs2_raw_inode *node)
+{
+ if (node->data_crc != crc32_no_comp(0, (unsigned char *)
+ ((int) &node->node_crc + sizeof (node->node_crc)),
+ node->csize)) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+#if defined(CONFIG_SYS_JFFS2_SORT_FRAGMENTS)
+/* External merge sort. */
+int sort_list(struct b_list *list);
+#endif
+#endif /* jffs2_private.h */
diff --git a/roms/u-boot/fs/jffs2/mergesort.c b/roms/u-boot/fs/jffs2/mergesort.c
new file mode 100644
index 000000000..fca77aa65
--- /dev/null
+++ b/roms/u-boot/fs/jffs2/mergesort.c
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: MIT
+/*
+ * This file is copyright 2001 Simon Tatham.
+ * Rewritten from original source 2006 by Dan Merillat for use in u-boot.
+ *
+ * Original code can be found at:
+ * http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html
+ */
+
+#include <common.h>
+#include "jffs2_private.h"
+
+int sort_list(struct b_list *list)
+{
+ struct b_node *p, *q, *e, **tail;
+ int k, psize, qsize;
+
+ if (!list->listHead)
+ return 0;
+
+ for (k = 1; k < list->listCount; k *= 2) {
+ tail = &list->listHead;
+ for (p = q = list->listHead; p; p = q) {
+ /* step 'k' places from p; */
+ for (psize = 0; q && psize < k; psize++)
+ q = q->next;
+ qsize = k;
+
+ /* two lists, merge them. */
+ while (psize || (qsize && q)) {
+ /* merge the next element */
+ if (psize == 0 ||
+ ((qsize && q) &&
+ list->listCompare(p, q))) {
+ /* p is empty, or p > q, so q next */
+ e = q;
+ q = q->next;
+ qsize--;
+ } else {
+ e = p;
+ p = p->next;
+ psize--;
+ }
+ e->next = NULL; /* break accidental loops. */
+ *tail = e;
+ tail = &e->next;
+ }
+ }
+ }
+ return 0;
+}
diff --git a/roms/u-boot/fs/jffs2/mini_inflate.c b/roms/u-boot/fs/jffs2/mini_inflate.c
new file mode 100644
index 000000000..3072eedd8
--- /dev/null
+++ b/roms/u-boot/fs/jffs2/mini_inflate.c
@@ -0,0 +1,375 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*-------------------------------------------------------------------------
+ * Filename: mini_inflate.c
+ * Version: $Id: mini_inflate.c,v 1.3 2002/01/24 22:58:42 rfeany Exp $
+ * Copyright: Copyright (C) 2001, Russ Dill
+ * Author: Russ Dill <Russ.Dill@asu.edu>
+ * Description: Mini inflate implementation (RFC 1951)
+ *-----------------------------------------------------------------------*/
+
+#include <config.h>
+#include <jffs2/mini_inflate.h>
+
+/* The order that the code lengths in section 3.2.7 are in */
+static unsigned char huffman_order[] = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5,
+ 11, 4, 12, 3, 13, 2, 14, 1, 15};
+
+static inline void cramfs_memset(int *s, const int c, size n)
+{
+ n--;
+ for (;n > 0; n--) s[n] = c;
+ s[0] = c;
+}
+
+/* associate a stream with a block of data and reset the stream */
+static void init_stream(struct bitstream *stream, unsigned char *data,
+ void *(*inflate_memcpy)(void *, const void *, size))
+{
+ stream->error = NO_ERROR;
+ stream->memcpy = inflate_memcpy;
+ stream->decoded = 0;
+ stream->data = data;
+ stream->bit = 0; /* The first bit of the stream is the lsb of the
+ * first byte */
+
+ /* really sorry about all this initialization, think of a better way,
+ * let me know and it will get cleaned up */
+ stream->codes.bits = 8;
+ stream->codes.num_symbols = 19;
+ stream->codes.lengths = stream->code_lengths;
+ stream->codes.symbols = stream->code_symbols;
+ stream->codes.count = stream->code_count;
+ stream->codes.first = stream->code_first;
+ stream->codes.pos = stream->code_pos;
+
+ stream->lengths.bits = 16;
+ stream->lengths.num_symbols = 288;
+ stream->lengths.lengths = stream->length_lengths;
+ stream->lengths.symbols = stream->length_symbols;
+ stream->lengths.count = stream->length_count;
+ stream->lengths.first = stream->length_first;
+ stream->lengths.pos = stream->length_pos;
+
+ stream->distance.bits = 16;
+ stream->distance.num_symbols = 32;
+ stream->distance.lengths = stream->distance_lengths;
+ stream->distance.symbols = stream->distance_symbols;
+ stream->distance.count = stream->distance_count;
+ stream->distance.first = stream->distance_first;
+ stream->distance.pos = stream->distance_pos;
+
+}
+
+/* pull 'bits' bits out of the stream. The last bit pulled it returned as the
+ * msb. (section 3.1.1)
+ */
+static inline unsigned long pull_bits(struct bitstream *stream,
+ const unsigned int bits)
+{
+ unsigned long ret;
+ int i;
+
+ ret = 0;
+ for (i = 0; i < bits; i++) {
+ ret += ((*(stream->data) >> stream->bit) & 1) << i;
+
+ /* if, before incrementing, we are on bit 7,
+ * go to the lsb of the next byte */
+ if (stream->bit++ == 7) {
+ stream->bit = 0;
+ stream->data++;
+ }
+ }
+ return ret;
+}
+
+static inline int pull_bit(struct bitstream *stream)
+{
+ int ret = ((*(stream->data) >> stream->bit) & 1);
+ if (stream->bit++ == 7) {
+ stream->bit = 0;
+ stream->data++;
+ }
+ return ret;
+}
+
+/* discard bits up to the next whole byte */
+static void discard_bits(struct bitstream *stream)
+{
+ if (stream->bit != 0) {
+ stream->bit = 0;
+ stream->data++;
+ }
+}
+
+/* No decompression, the data is all literals (section 3.2.4) */
+static void decompress_none(struct bitstream *stream, unsigned char *dest)
+{
+ unsigned int length;
+
+ discard_bits(stream);
+ length = *(stream->data++);
+ length += *(stream->data++) << 8;
+ pull_bits(stream, 16); /* throw away the inverse of the size */
+
+ stream->decoded += length;
+ stream->memcpy(dest, stream->data, length);
+ stream->data += length;
+}
+
+/* Read in a symbol from the stream (section 3.2.2) */
+static int read_symbol(struct bitstream *stream, struct huffman_set *set)
+{
+ int bits = 0;
+ int code = 0;
+ while (!(set->count[bits] && code < set->first[bits] +
+ set->count[bits])) {
+ code = (code << 1) + pull_bit(stream);
+ if (++bits > set->bits) {
+ /* error decoding (corrupted data?) */
+ stream->error = CODE_NOT_FOUND;
+ return -1;
+ }
+ }
+ return set->symbols[set->pos[bits] + code - set->first[bits]];
+}
+
+/* decompress a stream of data encoded with the passed length and distance
+ * huffman codes */
+static void decompress_huffman(struct bitstream *stream, unsigned char *dest)
+{
+ struct huffman_set *lengths = &(stream->lengths);
+ struct huffman_set *distance = &(stream->distance);
+
+ int symbol, length, dist, i;
+
+ do {
+ if ((symbol = read_symbol(stream, lengths)) < 0) return;
+ if (symbol < 256) {
+ *(dest++) = symbol; /* symbol is a literal */
+ stream->decoded++;
+ } else if (symbol > 256) {
+ /* Determine the length of the repitition
+ * (section 3.2.5) */
+ if (symbol < 265) length = symbol - 254;
+ else if (symbol == 285) length = 258;
+ else {
+ length = pull_bits(stream, (symbol - 261) >> 2);
+ length += (4 << ((symbol - 261) >> 2)) + 3;
+ length += ((symbol - 1) % 4) <<
+ ((symbol - 261) >> 2);
+ }
+
+ /* Determine how far back to go */
+ if ((symbol = read_symbol(stream, distance)) < 0)
+ return;
+ if (symbol < 4) dist = symbol + 1;
+ else {
+ dist = pull_bits(stream, (symbol - 2) >> 1);
+ dist += (2 << ((symbol - 2) >> 1)) + 1;
+ dist += (symbol % 2) << ((symbol - 2) >> 1);
+ }
+ stream->decoded += length;
+ for (i = 0; i < length; i++) {
+ *dest = dest[-dist];
+ dest++;
+ }
+ }
+ } while (symbol != 256); /* 256 is the end of the data block */
+}
+
+/* Fill the lookup tables (section 3.2.2) */
+static void fill_code_tables(struct huffman_set *set)
+{
+ int code = 0, i, length;
+
+ /* fill in the first code of each bit length, and the pos pointer */
+ set->pos[0] = 0;
+ for (i = 1; i < set->bits; i++) {
+ code = (code + set->count[i - 1]) << 1;
+ set->first[i] = code;
+ set->pos[i] = set->pos[i - 1] + set->count[i - 1];
+ }
+
+ /* Fill in the table of symbols in order of their huffman code */
+ for (i = 0; i < set->num_symbols; i++) {
+ if ((length = set->lengths[i]))
+ set->symbols[set->pos[length]++] = i;
+ }
+
+ /* reset the pos pointer */
+ for (i = 1; i < set->bits; i++) set->pos[i] -= set->count[i];
+}
+
+static void init_code_tables(struct huffman_set *set)
+{
+ cramfs_memset(set->lengths, 0, set->num_symbols);
+ cramfs_memset(set->count, 0, set->bits);
+ cramfs_memset(set->first, 0, set->bits);
+}
+
+/* read in the huffman codes for dynamic decoding (section 3.2.7) */
+static void decompress_dynamic(struct bitstream *stream, unsigned char *dest)
+{
+ /* I tried my best to minimize the memory footprint here, while still
+ * keeping up performance. I really dislike the _lengths[] tables, but
+ * I see no way of eliminating them without a sizable performance
+ * impact. The first struct table keeps track of stats on each bit
+ * length. The _length table keeps a record of the bit length of each
+ * symbol. The _symbols table is for looking up symbols by the huffman
+ * code (the pos element points to the first place in the symbol table
+ * where that bit length occurs). I also hate the initization of these
+ * structs, if someone knows how to compact these, lemme know. */
+
+ struct huffman_set *codes = &(stream->codes);
+ struct huffman_set *lengths = &(stream->lengths);
+ struct huffman_set *distance = &(stream->distance);
+
+ int hlit = pull_bits(stream, 5) + 257;
+ int hdist = pull_bits(stream, 5) + 1;
+ int hclen = pull_bits(stream, 4) + 4;
+ int length, curr_code, symbol, i, last_code;
+
+ last_code = 0;
+
+ init_code_tables(codes);
+ init_code_tables(lengths);
+ init_code_tables(distance);
+
+ /* fill in the count of each bit length' as well as the lengths
+ * table */
+ for (i = 0; i < hclen; i++) {
+ length = pull_bits(stream, 3);
+ codes->lengths[huffman_order[i]] = length;
+ if (length) codes->count[length]++;
+
+ }
+ fill_code_tables(codes);
+
+ /* Do the same for the length codes, being carefull of wrap through
+ * to the distance table */
+ curr_code = 0;
+ while (curr_code < hlit) {
+ if ((symbol = read_symbol(stream, codes)) < 0) return;
+ if (symbol == 0) {
+ curr_code++;
+ last_code = 0;
+ } else if (symbol < 16) { /* Literal length */
+ lengths->lengths[curr_code] = last_code = symbol;
+ lengths->count[symbol]++;
+ curr_code++;
+ } else if (symbol == 16) { /* repeat the last symbol 3 - 6
+ * times */
+ length = 3 + pull_bits(stream, 2);
+ for (;length; length--, curr_code++)
+ if (curr_code < hlit) {
+ lengths->lengths[curr_code] =
+ last_code;
+ lengths->count[last_code]++;
+ } else { /* wrap to the distance table */
+ distance->lengths[curr_code - hlit] =
+ last_code;
+ distance->count[last_code]++;
+ }
+ } else if (symbol == 17) { /* repeat a bit length 0 */
+ curr_code += 3 + pull_bits(stream, 3);
+ last_code = 0;
+ } else { /* same, but more times */
+ curr_code += 11 + pull_bits(stream, 7);
+ last_code = 0;
+ }
+ }
+ fill_code_tables(lengths);
+
+ /* Fill the distance table, don't need to worry about wrapthrough
+ * here */
+ curr_code -= hlit;
+ while (curr_code < hdist) {
+ if ((symbol = read_symbol(stream, codes)) < 0) return;
+ if (symbol == 0) {
+ curr_code++;
+ last_code = 0;
+ } else if (symbol < 16) {
+ distance->lengths[curr_code] = last_code = symbol;
+ distance->count[symbol]++;
+ curr_code++;
+ } else if (symbol == 16) {
+ length = 3 + pull_bits(stream, 2);
+ for (;length; length--, curr_code++) {
+ distance->lengths[curr_code] =
+ last_code;
+ distance->count[last_code]++;
+ }
+ } else if (symbol == 17) {
+ curr_code += 3 + pull_bits(stream, 3);
+ last_code = 0;
+ } else {
+ curr_code += 11 + pull_bits(stream, 7);
+ last_code = 0;
+ }
+ }
+ fill_code_tables(distance);
+
+ decompress_huffman(stream, dest);
+}
+
+/* fill in the length and distance huffman codes for fixed encoding
+ * (section 3.2.6) */
+static void decompress_fixed(struct bitstream *stream, unsigned char *dest)
+{
+ /* let gcc fill in the initial values */
+ struct huffman_set *lengths = &(stream->lengths);
+ struct huffman_set *distance = &(stream->distance);
+
+ cramfs_memset(lengths->count, 0, 16);
+ cramfs_memset(lengths->first, 0, 16);
+ cramfs_memset(lengths->lengths, 8, 144);
+ cramfs_memset(lengths->lengths + 144, 9, 112);
+ cramfs_memset(lengths->lengths + 256, 7, 24);
+ cramfs_memset(lengths->lengths + 280, 8, 8);
+ lengths->count[7] = 24;
+ lengths->count[8] = 152;
+ lengths->count[9] = 112;
+
+ cramfs_memset(distance->count, 0, 16);
+ cramfs_memset(distance->first, 0, 16);
+ cramfs_memset(distance->lengths, 5, 32);
+ distance->count[5] = 32;
+
+
+ fill_code_tables(lengths);
+ fill_code_tables(distance);
+
+
+ decompress_huffman(stream, dest);
+}
+
+/* returns the number of bytes decoded, < 0 if there was an error. Note that
+ * this function assumes that the block starts on a byte boundry
+ * (non-compliant, but I don't see where this would happen). section 3.2.3 */
+long decompress_block(unsigned char *dest, unsigned char *source,
+ void *(*inflate_memcpy)(void *, const void *, size))
+{
+ int bfinal, btype;
+ struct bitstream stream;
+
+ init_stream(&stream, source, inflate_memcpy);
+ do {
+ bfinal = pull_bit(&stream);
+ btype = pull_bits(&stream, 2);
+ if (btype == NO_COMP) decompress_none(&stream, dest + stream.decoded);
+ else if (btype == DYNAMIC_COMP)
+ decompress_dynamic(&stream, dest + stream.decoded);
+ else if (btype == FIXED_COMP) decompress_fixed(&stream, dest + stream.decoded);
+ else stream.error = COMP_UNKNOWN;
+ } while (!bfinal && !stream.error);
+
+#if 0
+ putstr("decompress_block start\r\n");
+ putLabeledWord("stream.error = ",stream.error);
+ putLabeledWord("stream.decoded = ",stream.decoded);
+ putLabeledWord("dest = ",dest);
+ putstr("decompress_block end\r\n");
+#endif
+ return stream.error ? -stream.error : stream.decoded;
+}
diff --git a/roms/u-boot/fs/jffs2/summary.h b/roms/u-boot/fs/jffs2/summary.h
new file mode 100644
index 000000000..834933cd1
--- /dev/null
+++ b/roms/u-boot/fs/jffs2/summary.h
@@ -0,0 +1,163 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright © 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>,
+ * Zoltan Sogor <weth@inf.u-szeged.hu>,
+ * Patrik Kluba <pajko@halom.u-szeged.hu>,
+ * University of Szeged, Hungary
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ */
+
+#ifndef JFFS2_SUMMARY_H
+#define JFFS2_SUMMARY_H
+
+#define BLK_STATE_ALLFF 0
+#define BLK_STATE_CLEAN 1
+#define BLK_STATE_PARTDIRTY 2
+#define BLK_STATE_CLEANMARKER 3
+#define BLK_STATE_ALLDIRTY 4
+#define BLK_STATE_BADBLOCK 5
+
+#define JFFS2_SUMMARY_NOSUM_SIZE 0xffffffff
+#define JFFS2_SUMMARY_INODE_SIZE (sizeof(struct jffs2_sum_inode_flash))
+#define JFFS2_SUMMARY_DIRENT_SIZE(x) (sizeof(struct jffs2_sum_dirent_flash) + (x))
+#define JFFS2_SUMMARY_XATTR_SIZE (sizeof(struct jffs2_sum_xattr_flash))
+#define JFFS2_SUMMARY_XREF_SIZE (sizeof(struct jffs2_sum_xref_flash))
+
+/* Summary structures used on flash */
+
+struct jffs2_sum_unknown_flash
+{
+ __u16 nodetype; /* node type */
+};
+
+struct jffs2_sum_inode_flash
+{
+ __u16 nodetype; /* node type */
+ __u32 inode; /* inode number */
+ __u32 version; /* inode version */
+ __u32 offset; /* offset on jeb */
+ __u32 totlen; /* record length */
+} __attribute__((packed));
+
+struct jffs2_sum_dirent_flash
+{
+ __u16 nodetype; /* == JFFS_NODETYPE_DIRENT */
+ __u32 totlen; /* record length */
+ __u32 offset; /* offset on jeb */
+ __u32 pino; /* parent inode */
+ __u32 version; /* dirent version */
+ __u32 ino; /* == zero for unlink */
+ uint8_t nsize; /* dirent name size */
+ uint8_t type; /* dirent type */
+ uint8_t name[0]; /* dirent name */
+} __attribute__((packed));
+
+struct jffs2_sum_xattr_flash
+{
+ __u16 nodetype; /* == JFFS2_NODETYPE_XATR */
+ __u32 xid; /* xattr identifier */
+ __u32 version; /* version number */
+ __u32 offset; /* offset on jeb */
+ __u32 totlen; /* node length */
+} __attribute__((packed));
+
+struct jffs2_sum_xref_flash
+{
+ __u16 nodetype; /* == JFFS2_NODETYPE_XREF */
+ __u32 offset; /* offset on jeb */
+} __attribute__((packed));
+
+union jffs2_sum_flash
+{
+ struct jffs2_sum_unknown_flash u;
+ struct jffs2_sum_inode_flash i;
+ struct jffs2_sum_dirent_flash d;
+ struct jffs2_sum_xattr_flash x;
+ struct jffs2_sum_xref_flash r;
+};
+
+/* Summary structures used in the memory */
+
+struct jffs2_sum_unknown_mem
+{
+ union jffs2_sum_mem *next;
+ __u16 nodetype; /* node type */
+};
+
+struct jffs2_sum_inode_mem
+{
+ union jffs2_sum_mem *next;
+ __u16 nodetype; /* node type */
+ __u32 inode; /* inode number */
+ __u32 version; /* inode version */
+ __u32 offset; /* offset on jeb */
+ __u32 totlen; /* record length */
+} __attribute__((packed));
+
+struct jffs2_sum_dirent_mem
+{
+ union jffs2_sum_mem *next;
+ __u16 nodetype; /* == JFFS_NODETYPE_DIRENT */
+ __u32 totlen; /* record length */
+ __u32 offset; /* ofset on jeb */
+ __u32 pino; /* parent inode */
+ __u32 version; /* dirent version */
+ __u32 ino; /* == zero for unlink */
+ uint8_t nsize; /* dirent name size */
+ uint8_t type; /* dirent type */
+ uint8_t name[0]; /* dirent name */
+} __attribute__((packed));
+
+struct jffs2_sum_xattr_mem
+{
+ union jffs2_sum_mem *next;
+ __u16 nodetype;
+ __u32 xid;
+ __u32 version;
+ __u32 offset;
+ __u32 totlen;
+} __attribute__((packed));
+
+struct jffs2_sum_xref_mem
+{
+ union jffs2_sum_mem *next;
+ __u16 nodetype;
+ __u32 offset;
+} __attribute__((packed));
+
+union jffs2_sum_mem
+{
+ struct jffs2_sum_unknown_mem u;
+ struct jffs2_sum_inode_mem i;
+ struct jffs2_sum_dirent_mem d;
+ struct jffs2_sum_xattr_mem x;
+ struct jffs2_sum_xref_mem r;
+};
+
+/* Summary related information stored in superblock */
+
+struct jffs2_summary
+{
+ uint32_t sum_size; /* collected summary information for nextblock */
+ uint32_t sum_num;
+ uint32_t sum_padded;
+ union jffs2_sum_mem *sum_list_head;
+ union jffs2_sum_mem *sum_list_tail;
+
+ __u32 *sum_buf; /* buffer for writing out summary */
+};
+
+/* Summary marker is stored at the end of every sumarized erase block */
+
+struct jffs2_sum_marker
+{
+ __u32 offset; /* offset of the summary node in the jeb */
+ __u32 magic; /* == JFFS2_SUM_MAGIC */
+};
+
+#define JFFS2_SUMMARY_FRAME_SIZE (sizeof(struct jffs2_raw_summary) + sizeof(struct jffs2_sum_marker))
+
+#endif /* JFFS2_SUMMARY_H */
diff --git a/roms/u-boot/fs/reiserfs/Kconfig b/roms/u-boot/fs/reiserfs/Kconfig
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/u-boot/fs/reiserfs/Kconfig
diff --git a/roms/u-boot/fs/reiserfs/Makefile b/roms/u-boot/fs/reiserfs/Makefile
new file mode 100644
index 000000000..63dd89575
--- /dev/null
+++ b/roms/u-boot/fs/reiserfs/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright 2006
+# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+#
+# (C) Copyright 2003
+# Pavel Bartusek, Sysgo Real-Time Solutions AG, pba@sysgo.de
+#
+
+obj-y := reiserfs.o dev.o mode_string.o
diff --git a/roms/u-boot/fs/reiserfs/dev.c b/roms/u-boot/fs/reiserfs/dev.c
new file mode 100644
index 000000000..5d9c26489
--- /dev/null
+++ b/roms/u-boot/fs/reiserfs/dev.c
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2003 - 2004
+ * Sysgo AG, <www.elinos.com>, Pavel Bartusek <pba@sysgo.com>
+ */
+
+
+#include <common.h>
+#include <config.h>
+#include <reiserfs.h>
+#include <fs_internal.h>
+#include "reiserfs_private.h"
+
+static struct blk_desc *reiserfs_blk_desc;
+static struct disk_partition *part_info;
+
+
+void reiserfs_set_blk_dev(struct blk_desc *rbdd, struct disk_partition *info)
+{
+ reiserfs_blk_desc = rbdd;
+ part_info = info;
+}
+
+int reiserfs_devread(int sector, int byte_offset, int byte_len, char *buf)
+{
+ return fs_devread(reiserfs_blk_desc, part_info, sector, byte_offset,
+ byte_len, buf);
+}
diff --git a/roms/u-boot/fs/reiserfs/mode_string.c b/roms/u-boot/fs/reiserfs/mode_string.c
new file mode 100644
index 000000000..638cf6368
--- /dev/null
+++ b/roms/u-boot/fs/reiserfs/mode_string.c
@@ -0,0 +1,124 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * mode_string implementation for busybox
+ *
+ * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
+ */
+
+/* Aug 13, 2003
+ * Fix a bug reported by junkio@cox.net involving the mode_chars index.
+ */
+
+
+#include <common.h>
+#include <linux/stat.h>
+
+#if ( S_ISUID != 04000 ) || ( S_ISGID != 02000 ) || ( S_ISVTX != 01000 ) \
+ || ( S_IRUSR != 00400 ) || ( S_IWUSR != 00200 ) || ( S_IXUSR != 00100 ) \
+ || ( S_IRGRP != 00040 ) || ( S_IWGRP != 00020 ) || ( S_IXGRP != 00010 ) \
+ || ( S_IROTH != 00004 ) || ( S_IWOTH != 00002 ) || ( S_IXOTH != 00001 )
+#error permission bitflag value assumption(s) violated!
+#endif
+
+#if ( S_IFSOCK!= 0140000 ) || ( S_IFLNK != 0120000 ) \
+ || ( S_IFREG != 0100000 ) || ( S_IFBLK != 0060000 ) \
+ || ( S_IFDIR != 0040000 ) || ( S_IFCHR != 0020000 ) \
+ || ( S_IFIFO != 0010000 )
+#warning mode type bitflag value assumption(s) violated! falling back to larger version
+
+#if (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX) == 07777
+#undef mode_t
+#define mode_t unsigned short
+#endif
+
+static const mode_t mode_flags[] = {
+ S_IRUSR, S_IWUSR, S_IXUSR, S_ISUID,
+ S_IRGRP, S_IWGRP, S_IXGRP, S_ISGID,
+ S_IROTH, S_IWOTH, S_IXOTH, S_ISVTX
+};
+
+/* The static const char arrays below are duplicated for the two cases
+ * because moving them ahead of the mode_flags declaration cause a text
+ * size increase with the gcc version I'm using. */
+
+/* The previous version used "0pcCd?bB-?l?s???". However, the '0', 'C',
+ * and 'B' types don't appear to be available on linux. So I removed them. */
+static const char type_chars[16] = "?pc?d?b?-?l?s???";
+/* 0123456789abcdef */
+static const char mode_chars[7] = "rwxSTst";
+
+const char *bb_mode_string(int mode)
+{
+ static char buf[12];
+ char *p = buf;
+
+ int i, j, k;
+
+ *p = type_chars[ (mode >> 12) & 0xf ];
+ i = 0;
+ do {
+ j = k = 0;
+ do {
+ *++p = '-';
+ if (mode & mode_flags[i+j]) {
+ *p = mode_chars[j];
+ k = j;
+ }
+ } while (++j < 3);
+ if (mode & mode_flags[i+j]) {
+ *p = mode_chars[3 + (k & 2) + ((i&8) >> 3)];
+ }
+ i += 4;
+ } while (i < 12);
+
+ /* Note: We don't bother with nul termination because bss initialization
+ * should have taken care of that for us. If the user scribbled in buf
+ * memory, they deserve whatever happens. But we'll at least assert. */
+ if (buf[10] != 0) return NULL;
+
+ return buf;
+}
+
+#else
+
+/* The previous version used "0pcCd?bB-?l?s???". However, the '0', 'C',
+ * and 'B' types don't appear to be available on linux. So I removed them. */
+static const char type_chars[16] = "?pc?d?b?-?l?s???";
+/* 0123456789abcdef */
+static const char mode_chars[7] = "rwxSTst";
+
+const char *bb_mode_string(int mode)
+{
+ static char buf[12];
+ char *p = buf;
+
+ int i, j, k, m;
+
+ *p = type_chars[ (mode >> 12) & 0xf ];
+ i = 0;
+ m = 0400;
+ do {
+ j = k = 0;
+ do {
+ *++p = '-';
+ if (mode & m) {
+ *p = mode_chars[j];
+ k = j;
+ }
+ m >>= 1;
+ } while (++j < 3);
+ ++i;
+ if (mode & (010000 >> i)) {
+ *p = mode_chars[3 + (k & 2) + (i == 3)];
+ }
+ } while (i < 3);
+
+ /* Note: We don't bother with nul termination because bss initialization
+ * should have taken care of that for us. If the user scribbled in buf
+ * memory, they deserve whatever happens. But we'll at least assert. */
+ if (buf[10] != 0) return NULL;
+
+ return buf;
+}
+
+#endif
diff --git a/roms/u-boot/fs/reiserfs/reiserfs.c b/roms/u-boot/fs/reiserfs/reiserfs.c
new file mode 100644
index 000000000..635aab2eb
--- /dev/null
+++ b/roms/u-boot/fs/reiserfs/reiserfs.c
@@ -0,0 +1,971 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2000-2002 by Hans Reiser, licensing governed by reiserfs/README
+ *
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2000, 2001 Free Software Foundation, Inc.
+ *
+ * (C) Copyright 2003 - 2004
+ * Sysgo AG, <www.elinos.com>, Pavel Bartusek <pba@sysgo.com>
+ *
+ */
+
+/* An implementation for the ReiserFS filesystem ported from GRUB.
+ * Some parts of this code (mainly the structures and defines) are
+ * from the original reiser fs code, as found in the linux kernel.
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <linux/ctype.h>
+#include <linux/time.h>
+#include <asm/byteorder.h>
+#include <reiserfs.h>
+
+#include "reiserfs_private.h"
+
+#undef REISERDEBUG
+
+/* Some parts of this code (mainly the structures and defines) are
+ * from the original reiser fs code, as found in the linux kernel.
+ */
+
+static char fsys_buf[FSYS_BUFLEN];
+static reiserfs_error_t errnum = ERR_NONE;
+static int print_possibilities;
+static unsigned int filepos, filemax;
+
+static int
+substring (const char *s1, const char *s2)
+{
+ while (*s1 == *s2)
+ {
+ /* The strings match exactly. */
+ if (! *(s1++))
+ return 0;
+ s2 ++;
+ }
+
+ /* S1 is a substring of S2. */
+ if (*s1 == 0)
+ return -1;
+
+ /* S1 isn't a substring. */
+ return 1;
+}
+
+static void sd_print_item (struct item_head * ih, char * item)
+{
+ char filetime[30];
+ time_t ttime;
+
+ if (stat_data_v1 (ih)) {
+ struct stat_data_v1 * sd = (struct stat_data_v1 *)item;
+ ttime = sd_v1_mtime(sd);
+ ctime_r(&ttime, filetime);
+ printf ("%-10s %4hd %6d %6d %9d %24.24s",
+ bb_mode_string(sd_v1_mode(sd)), sd_v1_nlink(sd),sd_v1_uid(sd), sd_v1_gid(sd),
+ sd_v1_size(sd), filetime);
+ } else {
+ struct stat_data * sd = (struct stat_data *)item;
+ ttime = sd_v2_mtime(sd);
+ ctime_r(&ttime, filetime);
+ printf ("%-10s %4d %6d %6d %9d %24.24s",
+ bb_mode_string(sd_v2_mode(sd)), sd_v2_nlink(sd),sd_v2_uid(sd),sd_v2_gid(sd),
+ (__u32) sd_v2_size(sd), filetime);
+ }
+}
+
+static int
+journal_read (int block, int len, char *buffer)
+{
+ return reiserfs_devread ((INFO->journal_block + block) << INFO->blocksize_shift,
+ 0, len, buffer);
+}
+
+/* Read a block from ReiserFS file system, taking the journal into
+ * account. If the block nr is in the journal, the block from the
+ * journal taken.
+ */
+static int
+block_read (unsigned int blockNr, int start, int len, char *buffer)
+{
+ int transactions = INFO->journal_transactions;
+ int desc_block = INFO->journal_first_desc;
+ int journal_mask = INFO->journal_block_count - 1;
+ int translatedNr = blockNr;
+ __u32 *journal_table = JOURNAL_START;
+ while (transactions-- > 0)
+ {
+ int i = 0;
+ int j_len;
+ if (__le32_to_cpu(*journal_table) != 0xffffffff)
+ {
+ /* Search for the blockNr in cached journal */
+ j_len = __le32_to_cpu(*journal_table++);
+ while (i++ < j_len)
+ {
+ if (__le32_to_cpu(*journal_table++) == blockNr)
+ {
+ journal_table += j_len - i;
+ goto found;
+ }
+ }
+ }
+ else
+ {
+ /* This is the end of cached journal marker. The remaining
+ * transactions are still on disk.
+ */
+ struct reiserfs_journal_desc desc;
+ struct reiserfs_journal_commit commit;
+
+ if (! journal_read (desc_block, sizeof (desc), (char *) &desc))
+ return 0;
+
+ j_len = __le32_to_cpu(desc.j_len);
+ while (i < j_len && i < JOURNAL_TRANS_HALF)
+ if (__le32_to_cpu(desc.j_realblock[i++]) == blockNr)
+ goto found;
+
+ if (j_len >= JOURNAL_TRANS_HALF)
+ {
+ int commit_block = (desc_block + 1 + j_len) & journal_mask;
+ if (! journal_read (commit_block,
+ sizeof (commit), (char *) &commit))
+ return 0;
+ while (i < j_len)
+ if (__le32_to_cpu(commit.j_realblock[i++ - JOURNAL_TRANS_HALF]) == blockNr)
+ goto found;
+ }
+ }
+ goto not_found;
+
+ found:
+ translatedNr = INFO->journal_block + ((desc_block + i) & journal_mask);
+#ifdef REISERDEBUG
+ printf ("block_read: block %d is mapped to journal block %d.\n",
+ blockNr, translatedNr - INFO->journal_block);
+#endif
+ /* We must continue the search, as this block may be overwritten
+ * in later transactions.
+ */
+ not_found:
+ desc_block = (desc_block + 2 + j_len) & journal_mask;
+ }
+ return reiserfs_devread (translatedNr << INFO->blocksize_shift, start, len, buffer);
+}
+
+/* Init the journal data structure. We try to cache as much as
+ * possible in the JOURNAL_START-JOURNAL_END space, but if it is full
+ * we can still read the rest from the disk on demand.
+ *
+ * The first number of valid transactions and the descriptor block of the
+ * first valid transaction are held in INFO. The transactions are all
+ * adjacent, but we must take care of the journal wrap around.
+ */
+static int
+journal_init (void)
+{
+ unsigned int block_count = INFO->journal_block_count;
+ unsigned int desc_block;
+ unsigned int commit_block;
+ unsigned int next_trans_id;
+ struct reiserfs_journal_header header;
+ struct reiserfs_journal_desc desc;
+ struct reiserfs_journal_commit commit;
+ __u32 *journal_table = JOURNAL_START;
+
+ journal_read (block_count, sizeof (header), (char *) &header);
+ desc_block = __le32_to_cpu(header.j_first_unflushed_offset);
+ if (desc_block >= block_count)
+ return 0;
+
+ INFO->journal_first_desc = desc_block;
+ next_trans_id = __le32_to_cpu(header.j_last_flush_trans_id) + 1;
+
+#ifdef REISERDEBUG
+ printf ("journal_init: last flushed %d\n",
+ __le32_to_cpu(header.j_last_flush_trans_id));
+#endif
+
+ while (1)
+ {
+ journal_read (desc_block, sizeof (desc), (char *) &desc);
+ if (substring (JOURNAL_DESC_MAGIC, desc.j_magic) > 0
+ || __le32_to_cpu(desc.j_trans_id) != next_trans_id
+ || __le32_to_cpu(desc.j_mount_id) != __le32_to_cpu(header.j_mount_id))
+ /* no more valid transactions */
+ break;
+
+ commit_block = (desc_block + __le32_to_cpu(desc.j_len) + 1) & (block_count - 1);
+ journal_read (commit_block, sizeof (commit), (char *) &commit);
+ if (__le32_to_cpu(desc.j_trans_id) != commit.j_trans_id
+ || __le32_to_cpu(desc.j_len) != __le32_to_cpu(commit.j_len))
+ /* no more valid transactions */
+ break;
+
+#ifdef REISERDEBUG
+ printf ("Found valid transaction %d/%d at %d.\n",
+ __le32_to_cpu(desc.j_trans_id), __le32_to_cpu(desc.j_mount_id), desc_block);
+#endif
+
+ next_trans_id++;
+ if (journal_table < JOURNAL_END)
+ {
+ if ((journal_table + 1 + __le32_to_cpu(desc.j_len)) >= JOURNAL_END)
+ {
+ /* The table is almost full; mark the end of the cached
+ * journal.*/
+ *journal_table = __cpu_to_le32(0xffffffff);
+ journal_table = JOURNAL_END;
+ }
+ else
+ {
+ unsigned int i;
+ /* Cache the length and the realblock numbers in the table.
+ * The block number of descriptor can easily be computed.
+ * and need not to be stored here.
+ */
+
+ /* both are in the little endian format */
+ *journal_table++ = desc.j_len;
+ for (i = 0; i < __le32_to_cpu(desc.j_len) && i < JOURNAL_TRANS_HALF; i++)
+ {
+ /* both are in the little endian format */
+ *journal_table++ = desc.j_realblock[i];
+#ifdef REISERDEBUG
+ printf ("block %d is in journal %d.\n",
+ __le32_to_cpu(desc.j_realblock[i]), desc_block);
+#endif
+ }
+ for ( ; i < __le32_to_cpu(desc.j_len); i++)
+ {
+ /* both are in the little endian format */
+ *journal_table++ = commit.j_realblock[i-JOURNAL_TRANS_HALF];
+#ifdef REISERDEBUG
+ printf ("block %d is in journal %d.\n",
+ __le32_to_cpu(commit.j_realblock[i-JOURNAL_TRANS_HALF]),
+ desc_block);
+#endif
+ }
+ }
+ }
+ desc_block = (commit_block + 1) & (block_count - 1);
+ }
+#ifdef REISERDEBUG
+ printf ("Transaction %d/%d at %d isn't valid.\n",
+ __le32_to_cpu(desc.j_trans_id), __le32_to_cpu(desc.j_mount_id), desc_block);
+#endif
+
+ INFO->journal_transactions
+ = next_trans_id - __le32_to_cpu(header.j_last_flush_trans_id) - 1;
+ return errnum == 0;
+}
+
+/* check filesystem types and read superblock into memory buffer */
+int
+reiserfs_mount (unsigned part_length)
+{
+ struct reiserfs_super_block super;
+ int superblock = REISERFS_DISK_OFFSET_IN_BYTES >> SECTOR_BITS;
+ char *cache;
+
+ if (part_length < superblock + (sizeof (super) >> SECTOR_BITS)
+ || ! reiserfs_devread (superblock, 0, sizeof (struct reiserfs_super_block),
+ (char *) &super)
+ || (substring (REISER3FS_SUPER_MAGIC_STRING, super.s_magic) > 0
+ && substring (REISER2FS_SUPER_MAGIC_STRING, super.s_magic) > 0
+ && substring (REISERFS_SUPER_MAGIC_STRING, super.s_magic) > 0)
+ || (/* check that this is not a copy inside the journal log */
+ sb_journal_block(&super) * sb_blocksize(&super)
+ <= REISERFS_DISK_OFFSET_IN_BYTES))
+ {
+ /* Try old super block position */
+ superblock = REISERFS_OLD_DISK_OFFSET_IN_BYTES >> SECTOR_BITS;
+ if (part_length < superblock + (sizeof (super) >> SECTOR_BITS)
+ || ! reiserfs_devread (superblock, 0, sizeof (struct reiserfs_super_block),
+ (char *) &super))
+ return 0;
+
+ if (substring (REISER2FS_SUPER_MAGIC_STRING, super.s_magic) > 0
+ && substring (REISERFS_SUPER_MAGIC_STRING, super.s_magic) > 0)
+ {
+ /* pre journaling super block ? */
+ if (substring (REISERFS_SUPER_MAGIC_STRING,
+ (char*) ((int) &super + 20)) > 0)
+ return 0;
+
+ set_sb_blocksize(&super, REISERFS_OLD_BLOCKSIZE);
+ set_sb_journal_block(&super, 0);
+ set_sb_version(&super, 0);
+ }
+ }
+
+ /* check the version number. */
+ if (sb_version(&super) > REISERFS_MAX_SUPPORTED_VERSION)
+ return 0;
+
+ INFO->version = sb_version(&super);
+ INFO->blocksize = sb_blocksize(&super);
+ INFO->fullblocksize_shift = log2 (sb_blocksize(&super));
+ INFO->blocksize_shift = INFO->fullblocksize_shift - SECTOR_BITS;
+ INFO->cached_slots =
+ (FSYSREISER_CACHE_SIZE >> INFO->fullblocksize_shift) - 1;
+
+#ifdef REISERDEBUG
+ printf ("reiserfs_mount: version=%d, blocksize=%d\n",
+ INFO->version, INFO->blocksize);
+#endif /* REISERDEBUG */
+
+ /* Clear node cache. */
+ memset (INFO->blocks, 0, sizeof (INFO->blocks));
+
+ if (sb_blocksize(&super) < FSYSREISER_MIN_BLOCKSIZE
+ || sb_blocksize(&super) > FSYSREISER_MAX_BLOCKSIZE
+ || (SECTOR_SIZE << INFO->blocksize_shift) != sb_blocksize(&super))
+ return 0;
+
+ /* Initialize journal code. If something fails we end with zero
+ * journal_transactions, so we don't access the journal at all.
+ */
+ INFO->journal_transactions = 0;
+ if (sb_journal_block(&super) != 0 && super.s_journal_dev == 0)
+ {
+ INFO->journal_block = sb_journal_block(&super);
+ INFO->journal_block_count = sb_journal_size(&super);
+ if (is_power_of_two (INFO->journal_block_count))
+ journal_init ();
+
+ /* Read in super block again, maybe it is in the journal */
+ block_read (superblock >> INFO->blocksize_shift,
+ 0, sizeof (struct reiserfs_super_block), (char *) &super);
+ }
+
+ if (! block_read (sb_root_block(&super), 0, INFO->blocksize, (char*) ROOT))
+ return 0;
+
+ cache = ROOT;
+ INFO->tree_depth = __le16_to_cpu(BLOCKHEAD (cache)->blk_level);
+
+#ifdef REISERDEBUG
+ printf ("root read_in: block=%d, depth=%d\n",
+ sb_root_block(&super), INFO->tree_depth);
+#endif /* REISERDEBUG */
+
+ if (INFO->tree_depth >= MAX_HEIGHT)
+ return 0;
+ if (INFO->tree_depth == DISK_LEAF_NODE_LEVEL)
+ {
+ /* There is only one node in the whole filesystem,
+ * which is simultanously leaf and root */
+ memcpy (LEAF, ROOT, INFO->blocksize);
+ }
+ return 1;
+}
+
+/***************** TREE ACCESSING METHODS *****************************/
+
+/* I assume you are familiar with the ReiserFS tree, if not go to
+ * http://www.namesys.com/content_table.html
+ *
+ * My tree node cache is organized as following
+ * 0 ROOT node
+ * 1 LEAF node (if the ROOT is also a LEAF it is copied here
+ * 2-n other nodes on current path from bottom to top.
+ * if there is not enough space in the cache, the top most are
+ * omitted.
+ *
+ * I have only two methods to find a key in the tree:
+ * search_stat(dir_id, objectid) searches for the stat entry (always
+ * the first entry) of an object.
+ * next_key() gets the next key in tree order.
+ *
+ * This means, that I can only sequential reads of files are
+ * efficient, but this really doesn't hurt for grub.
+ */
+
+/* Read in the node at the current path and depth into the node cache.
+ * You must set INFO->blocks[depth] before.
+ */
+static char *
+read_tree_node (unsigned int blockNr, int depth)
+{
+ char* cache = CACHE(depth);
+ int num_cached = INFO->cached_slots;
+ if (depth < num_cached)
+ {
+ /* This is the cached part of the path. Check if same block is
+ * needed.
+ */
+ if (blockNr == INFO->blocks[depth])
+ return cache;
+ }
+ else
+ cache = CACHE(num_cached);
+
+#ifdef REISERDEBUG
+ printf (" next read_in: block=%d (depth=%d)\n",
+ blockNr, depth);
+#endif /* REISERDEBUG */
+ if (! block_read (blockNr, 0, INFO->blocksize, cache))
+ return 0;
+ /* Make sure it has the right node level */
+ if (__le16_to_cpu(BLOCKHEAD (cache)->blk_level) != depth)
+ {
+ errnum = ERR_FSYS_CORRUPT;
+ return 0;
+ }
+
+ INFO->blocks[depth] = blockNr;
+ return cache;
+}
+
+/* Get the next key, i.e. the key following the last retrieved key in
+ * tree order. INFO->current_ih and
+ * INFO->current_info are adapted accordingly. */
+static int
+next_key (void)
+{
+ int depth;
+ struct item_head *ih = INFO->current_ih + 1;
+ char *cache;
+
+#ifdef REISERDEBUG
+ printf ("next_key:\n old ih: key %d:%d:%d:%d version:%d\n",
+ __le32_to_cpu(INFO->current_ih->ih_key.k_dir_id),
+ __le32_to_cpu(INFO->current_ih->ih_key.k_objectid),
+ __le32_to_cpu(INFO->current_ih->ih_key.u.v1.k_offset),
+ __le32_to_cpu(INFO->current_ih->ih_key.u.v1.k_uniqueness),
+ __le16_to_cpu(INFO->current_ih->ih_version));
+#endif /* REISERDEBUG */
+
+ if (ih == &ITEMHEAD[__le16_to_cpu(BLOCKHEAD (LEAF)->blk_nr_item)])
+ {
+ depth = DISK_LEAF_NODE_LEVEL;
+ /* The last item, was the last in the leaf node.
+ * Read in the next block
+ */
+ do
+ {
+ if (depth == INFO->tree_depth)
+ {
+ /* There are no more keys at all.
+ * Return a dummy item with MAX_KEY */
+ ih = (struct item_head *) &BLOCKHEAD (LEAF)->blk_right_delim_key;
+ goto found;
+ }
+ depth++;
+#ifdef REISERDEBUG
+ printf (" depth=%d, i=%d\n", depth, INFO->next_key_nr[depth]);
+#endif /* REISERDEBUG */
+ }
+ while (INFO->next_key_nr[depth] == 0);
+
+ if (depth == INFO->tree_depth)
+ cache = ROOT;
+ else if (depth <= INFO->cached_slots)
+ cache = CACHE (depth);
+ else
+ {
+ cache = read_tree_node (INFO->blocks[depth], depth);
+ if (! cache)
+ return 0;
+ }
+
+ do
+ {
+ int nr_item = __le16_to_cpu(BLOCKHEAD (cache)->blk_nr_item);
+ int key_nr = INFO->next_key_nr[depth]++;
+#ifdef REISERDEBUG
+ printf (" depth=%d, i=%d/%d\n", depth, key_nr, nr_item);
+#endif /* REISERDEBUG */
+ if (key_nr == nr_item)
+ /* This is the last item in this block, set the next_key_nr to 0 */
+ INFO->next_key_nr[depth] = 0;
+
+ cache = read_tree_node (dc_block_number(&(DC (cache)[key_nr])), --depth);
+ if (! cache)
+ return 0;
+ }
+ while (depth > DISK_LEAF_NODE_LEVEL);
+
+ ih = ITEMHEAD;
+ }
+ found:
+ INFO->current_ih = ih;
+ INFO->current_item = &LEAF[__le16_to_cpu(ih->ih_item_location)];
+#ifdef REISERDEBUG
+ printf (" new ih: key %d:%d:%d:%d version:%d\n",
+ __le32_to_cpu(INFO->current_ih->ih_key.k_dir_id),
+ __le32_to_cpu(INFO->current_ih->ih_key.k_objectid),
+ __le32_to_cpu(INFO->current_ih->ih_key.u.v1.k_offset),
+ __le32_to_cpu(INFO->current_ih->ih_key.u.v1.k_uniqueness),
+ __le16_to_cpu(INFO->current_ih->ih_version));
+#endif /* REISERDEBUG */
+ return 1;
+}
+
+/* preconditions: reiserfs_mount already executed, therefore
+ * INFO block is valid
+ * returns: 0 if error (errnum is set),
+ * nonzero iff we were able to find the key successfully.
+ * postconditions: on a nonzero return, the current_ih and
+ * current_item fields describe the key that equals the
+ * searched key. INFO->next_key contains the next key after
+ * the searched key.
+ * side effects: messes around with the cache.
+ */
+static int
+search_stat (__u32 dir_id, __u32 objectid)
+{
+ char *cache;
+ int depth;
+ int nr_item;
+ int i;
+ struct item_head *ih;
+#ifdef REISERDEBUG
+ printf ("search_stat:\n key %d:%d:0:0\n", dir_id, objectid);
+#endif /* REISERDEBUG */
+
+ depth = INFO->tree_depth;
+ cache = ROOT;
+
+ while (depth > DISK_LEAF_NODE_LEVEL)
+ {
+ struct key *key;
+ nr_item = __le16_to_cpu(BLOCKHEAD (cache)->blk_nr_item);
+
+ key = KEY (cache);
+
+ for (i = 0; i < nr_item; i++)
+ {
+ if (__le32_to_cpu(key->k_dir_id) > dir_id
+ || (__le32_to_cpu(key->k_dir_id) == dir_id
+ && (__le32_to_cpu(key->k_objectid) > objectid
+ || (__le32_to_cpu(key->k_objectid) == objectid
+ && (__le32_to_cpu(key->u.v1.k_offset)
+ | __le32_to_cpu(key->u.v1.k_uniqueness)) > 0))))
+ break;
+ key++;
+ }
+
+#ifdef REISERDEBUG
+ printf (" depth=%d, i=%d/%d\n", depth, i, nr_item);
+#endif /* REISERDEBUG */
+ INFO->next_key_nr[depth] = (i == nr_item) ? 0 : i+1;
+ cache = read_tree_node (dc_block_number(&(DC (cache)[i])), --depth);
+ if (! cache)
+ return 0;
+ }
+
+ /* cache == LEAF */
+ nr_item = __le16_to_cpu(BLOCKHEAD (LEAF)->blk_nr_item);
+ ih = ITEMHEAD;
+ for (i = 0; i < nr_item; i++)
+ {
+ if (__le32_to_cpu(ih->ih_key.k_dir_id) == dir_id
+ && __le32_to_cpu(ih->ih_key.k_objectid) == objectid
+ && __le32_to_cpu(ih->ih_key.u.v1.k_offset) == 0
+ && __le32_to_cpu(ih->ih_key.u.v1.k_uniqueness) == 0)
+ {
+#ifdef REISERDEBUG
+ printf (" depth=%d, i=%d/%d\n", depth, i, nr_item);
+#endif /* REISERDEBUG */
+ INFO->current_ih = ih;
+ INFO->current_item = &LEAF[__le16_to_cpu(ih->ih_item_location)];
+ return 1;
+ }
+ ih++;
+ }
+ errnum = ERR_FSYS_CORRUPT;
+ return 0;
+}
+
+int
+reiserfs_read (char *buf, unsigned len)
+{
+ unsigned int blocksize;
+ unsigned int offset;
+ unsigned int to_read;
+ char *prev_buf = buf;
+
+#ifdef REISERDEBUG
+ printf ("reiserfs_read: filepos=%d len=%d, offset=%Lx\n",
+ filepos, len, (__u64) IH_KEY_OFFSET (INFO->current_ih) - 1);
+#endif /* REISERDEBUG */
+
+ if (__le32_to_cpu(INFO->current_ih->ih_key.k_objectid) != INFO->fileinfo.k_objectid
+ || IH_KEY_OFFSET (INFO->current_ih) > filepos + 1)
+ {
+ search_stat (INFO->fileinfo.k_dir_id, INFO->fileinfo.k_objectid);
+ goto get_next_key;
+ }
+
+ while (! errnum)
+ {
+ if (__le32_to_cpu(INFO->current_ih->ih_key.k_objectid) != INFO->fileinfo.k_objectid) {
+ break;
+ }
+
+ offset = filepos - IH_KEY_OFFSET (INFO->current_ih) + 1;
+ blocksize = __le16_to_cpu(INFO->current_ih->ih_item_len);
+
+#ifdef REISERDEBUG
+ printf (" loop: filepos=%d len=%d, offset=%d blocksize=%d\n",
+ filepos, len, offset, blocksize);
+#endif /* REISERDEBUG */
+
+ if (IH_KEY_ISTYPE(INFO->current_ih, TYPE_DIRECT)
+ && offset < blocksize)
+ {
+#ifdef REISERDEBUG
+ printf ("direct_read: offset=%d, blocksize=%d\n",
+ offset, blocksize);
+#endif /* REISERDEBUG */
+ to_read = blocksize - offset;
+ if (to_read > len)
+ to_read = len;
+
+ memcpy (buf, INFO->current_item + offset, to_read);
+ goto update_buf_len;
+ }
+ else if (IH_KEY_ISTYPE(INFO->current_ih, TYPE_INDIRECT))
+ {
+ blocksize = (blocksize >> 2) << INFO->fullblocksize_shift;
+#ifdef REISERDEBUG
+ printf ("indirect_read: offset=%d, blocksize=%d\n",
+ offset, blocksize);
+#endif /* REISERDEBUG */
+
+ while (offset < blocksize)
+ {
+ __u32 blocknr = __le32_to_cpu(((__u32 *) INFO->current_item)
+ [offset >> INFO->fullblocksize_shift]);
+ int blk_offset = offset & (INFO->blocksize-1);
+ to_read = INFO->blocksize - blk_offset;
+ if (to_read > len)
+ to_read = len;
+
+ /* Journal is only for meta data. Data blocks can be read
+ * directly without using block_read
+ */
+ reiserfs_devread (blocknr << INFO->blocksize_shift,
+ blk_offset, to_read, buf);
+ update_buf_len:
+ len -= to_read;
+ buf += to_read;
+ offset += to_read;
+ filepos += to_read;
+ if (len == 0)
+ goto done;
+ }
+ }
+ get_next_key:
+ next_key ();
+ }
+ done:
+ return errnum ? 0 : buf - prev_buf;
+}
+
+
+/* preconditions: reiserfs_mount already executed, therefore
+ * INFO block is valid
+ * returns: 0 if error, nonzero iff we were able to find the file successfully
+ * postconditions: on a nonzero return, INFO->fileinfo contains the info
+ * of the file we were trying to look up, filepos is 0 and filemax is
+ * the size of the file.
+ */
+static int
+reiserfs_dir (char *dirname)
+{
+ struct reiserfs_de_head *de_head;
+ char *rest, ch;
+ __u32 dir_id, objectid, parent_dir_id = 0, parent_objectid = 0;
+#ifndef STAGE1_5
+ int do_possibilities = 0;
+#endif /* ! STAGE1_5 */
+ char linkbuf[PATH_MAX]; /* buffer for following symbolic links */
+ int link_count = 0;
+ int mode;
+
+ dir_id = REISERFS_ROOT_PARENT_OBJECTID;
+ objectid = REISERFS_ROOT_OBJECTID;
+
+ while (1)
+ {
+#ifdef REISERDEBUG
+ printf ("dirname=%s\n", dirname);
+#endif /* REISERDEBUG */
+
+ /* Search for the stat info first. */
+ if (! search_stat (dir_id, objectid))
+ return 0;
+
+#ifdef REISERDEBUG
+ printf ("sd_mode=%x sd_size=%d\n",
+ stat_data_v1(INFO->current_ih) ? sd_v1_mode((struct stat_data_v1 *) INFO->current_item) :
+ sd_v2_mode((struct stat_data *) (INFO->current_item)),
+ stat_data_v1(INFO->current_ih) ? sd_v1_size((struct stat_data_v1 *) INFO->current_item) :
+ sd_v2_size((struct stat_data *) INFO->current_item)
+ );
+
+#endif /* REISERDEBUG */
+ mode = stat_data_v1(INFO->current_ih) ?
+ sd_v1_mode((struct stat_data_v1 *) INFO->current_item) :
+ sd_v2_mode((struct stat_data *) INFO->current_item);
+
+ /* If we've got a symbolic link, then chase it. */
+ if (S_ISLNK (mode))
+ {
+ unsigned int len;
+ if (++link_count > MAX_LINK_COUNT)
+ {
+ errnum = ERR_SYMLINK_LOOP;
+ return 0;
+ }
+
+ /* Get the symlink size. */
+ filemax = stat_data_v1(INFO->current_ih) ?
+ sd_v1_size((struct stat_data_v1 *) INFO->current_item) :
+ sd_v2_size((struct stat_data *) INFO->current_item);
+
+ /* Find out how long our remaining name is. */
+ len = 0;
+ while (dirname[len] && !isspace (dirname[len]))
+ len++;
+
+ if (filemax + len > sizeof (linkbuf) - 1)
+ {
+ errnum = ERR_FILELENGTH;
+ return 0;
+ }
+
+ /* Copy the remaining name to the end of the symlink data.
+ Note that DIRNAME and LINKBUF may overlap! */
+ memmove (linkbuf + filemax, dirname, len+1);
+
+ INFO->fileinfo.k_dir_id = dir_id;
+ INFO->fileinfo.k_objectid = objectid;
+ filepos = 0;
+ if (! next_key ()
+ || reiserfs_read (linkbuf, filemax) != filemax)
+ {
+ if (! errnum)
+ errnum = ERR_FSYS_CORRUPT;
+ return 0;
+ }
+
+#ifdef REISERDEBUG
+ printf ("symlink=%s\n", linkbuf);
+#endif /* REISERDEBUG */
+
+ dirname = linkbuf;
+ if (*dirname == '/')
+ {
+ /* It's an absolute link, so look it up in root. */
+ dir_id = REISERFS_ROOT_PARENT_OBJECTID;
+ objectid = REISERFS_ROOT_OBJECTID;
+ }
+ else
+ {
+ /* Relative, so look it up in our parent directory. */
+ dir_id = parent_dir_id;
+ objectid = parent_objectid;
+ }
+
+ /* Now lookup the new name. */
+ continue;
+ }
+
+ /* if we have a real file (and we're not just printing possibilities),
+ then this is where we want to exit */
+
+ if (! *dirname || isspace (*dirname))
+ {
+ if (! S_ISREG (mode))
+ {
+ errnum = ERR_BAD_FILETYPE;
+ return 0;
+ }
+
+ filepos = 0;
+ filemax = stat_data_v1(INFO->current_ih) ?
+ sd_v1_size((struct stat_data_v1 *) INFO->current_item) :
+ sd_v2_size((struct stat_data *) INFO->current_item);
+#if 0
+ /* If this is a new stat data and size is > 4GB set filemax to
+ * maximum
+ */
+ if (__le16_to_cpu(INFO->current_ih->ih_version) == ITEM_VERSION_2
+ && sd_size_hi((struct stat_data *) INFO->current_item) > 0)
+ filemax = 0xffffffff;
+#endif
+ INFO->fileinfo.k_dir_id = dir_id;
+ INFO->fileinfo.k_objectid = objectid;
+ return next_key ();
+ }
+
+ /* continue with the file/directory name interpretation */
+ while (*dirname == '/')
+ dirname++;
+ if (! S_ISDIR (mode))
+ {
+ errnum = ERR_BAD_FILETYPE;
+ return 0;
+ }
+ for (rest = dirname; (ch = *rest) && ! isspace (ch) && ch != '/'; rest++);
+ *rest = 0;
+
+# ifndef STAGE1_5
+ if (print_possibilities && ch != '/')
+ do_possibilities = 1;
+# endif /* ! STAGE1_5 */
+
+ while (1)
+ {
+ char *name_end;
+ int num_entries;
+
+ if (! next_key ())
+ return 0;
+#ifdef REISERDEBUG
+ printf ("ih: key %d:%d:%d:%d version:%d\n",
+ __le32_to_cpu(INFO->current_ih->ih_key.k_dir_id),
+ __le32_to_cpu(INFO->current_ih->ih_key.k_objectid),
+ __le32_to_cpu(INFO->current_ih->ih_key.u.v1.k_offset),
+ __le32_to_cpu(INFO->current_ih->ih_key.u.v1.k_uniqueness),
+ __le16_to_cpu(INFO->current_ih->ih_version));
+#endif /* REISERDEBUG */
+
+ if (__le32_to_cpu(INFO->current_ih->ih_key.k_objectid) != objectid)
+ break;
+
+ name_end = INFO->current_item + __le16_to_cpu(INFO->current_ih->ih_item_len);
+ de_head = (struct reiserfs_de_head *) INFO->current_item;
+ num_entries = __le16_to_cpu(INFO->current_ih->u.ih_entry_count);
+ while (num_entries > 0)
+ {
+ char *filename = INFO->current_item + deh_location(de_head);
+ char tmp = *name_end;
+ if ((deh_state(de_head) & DEH_Visible))
+ {
+ int cmp;
+ /* Directory names in ReiserFS are not null
+ * terminated. We write a temporary 0 behind it.
+ * NOTE: that this may overwrite the first block in
+ * the tree cache. That doesn't hurt as long as we
+ * don't call next_key () in between.
+ */
+ *name_end = 0;
+ cmp = substring (dirname, filename);
+ *name_end = tmp;
+# ifndef STAGE1_5
+ if (do_possibilities)
+ {
+ if (cmp <= 0)
+ {
+ char fn[PATH_MAX];
+ struct fsys_reiser_info info_save;
+
+ if (print_possibilities > 0)
+ print_possibilities = -print_possibilities;
+ *name_end = 0;
+ strcpy(fn, filename);
+ *name_end = tmp;
+
+ /* If NAME is "." or "..", do not count it. */
+ if (strcmp (fn, ".") != 0 && strcmp (fn, "..") != 0) {
+ memcpy(&info_save, INFO, sizeof(struct fsys_reiser_info));
+ search_stat (deh_dir_id(de_head), deh_objectid(de_head));
+ sd_print_item(INFO->current_ih, INFO->current_item);
+ printf(" %s\n", fn);
+ search_stat (dir_id, objectid);
+ memcpy(INFO, &info_save, sizeof(struct fsys_reiser_info));
+ }
+ }
+ }
+ else
+# endif /* ! STAGE1_5 */
+ if (cmp == 0)
+ goto found;
+ }
+ /* The beginning of this name marks the end of the next name.
+ */
+ name_end = filename;
+ de_head++;
+ num_entries--;
+ }
+ }
+
+# ifndef STAGE1_5
+ if (print_possibilities < 0)
+ return 1;
+# endif /* ! STAGE1_5 */
+
+ errnum = ERR_FILE_NOT_FOUND;
+ *rest = ch;
+ return 0;
+
+ found:
+ *rest = ch;
+ dirname = rest;
+
+ parent_dir_id = dir_id;
+ parent_objectid = objectid;
+ dir_id = deh_dir_id(de_head);
+ objectid = deh_objectid(de_head);
+ }
+}
+
+/*
+ * U-Boot interface functions
+ */
+
+/*
+ * List given directory
+ *
+ * RETURN: 0 - OK, else grub_error_t errnum
+ */
+int
+reiserfs_ls (char *dirname)
+{
+ char *dir_slash;
+ int res;
+
+ errnum = 0;
+ dir_slash = malloc(strlen(dirname) + 1);
+ if (dir_slash == NULL) {
+ return ERR_NUMBER_OVERFLOW;
+ }
+ strcpy(dir_slash, dirname);
+ /* add "/" to the directory name */
+ strcat(dir_slash, "/");
+
+ print_possibilities = 1;
+ res = reiserfs_dir (dir_slash);
+ free(dir_slash);
+ if (!res || errnum) {
+ return errnum;
+ }
+
+ return 0;
+}
+
+/*
+ * Open file for reading
+ *
+ * RETURN: >0 - OK, size of opened file
+ * <0 - ERROR -grub_error_t errnum
+ */
+int
+reiserfs_open (char *filename)
+{
+ /* open the file */
+ errnum = 0;
+ print_possibilities = 0;
+ if (!reiserfs_dir (filename) || errnum) {
+ return -errnum;
+ }
+ return filemax;
+}
diff --git a/roms/u-boot/fs/reiserfs/reiserfs_private.h b/roms/u-boot/fs/reiserfs/reiserfs_private.h
new file mode 100644
index 000000000..5936f2140
--- /dev/null
+++ b/roms/u-boot/fs/reiserfs/reiserfs_private.h
@@ -0,0 +1,509 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2000-2002 by Hans Reiser, licensing governed by reiserfs/README
+ *
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2000, 2001 Free Software Foundation, Inc.
+ *
+ * (C) Copyright 2003 - 2004
+ * Sysgo AG, <www.elinos.com>, Pavel Bartusek <pba@sysgo.com>
+ *
+ */
+
+/* An implementation for the ReiserFS filesystem ported from GRUB.
+ * Some parts of this code (mainly the structures and defines) are
+ * from the original reiser fs code, as found in the linux kernel.
+ */
+
+#include <compiler.h>
+
+#ifndef __BYTE_ORDER
+#if defined(__LITTLE_ENDIAN) && !defined(__BIG_ENDIAN)
+#define __BYTE_ORDER __LITTLE_ENDIAN
+#elif defined(__BIG_ENDIAN) && !defined(__LITTLE_ENDIAN)
+#define __BYTE_ORDER __BIG_ENDIAN
+#else
+#error "unable to define __BYTE_ORDER"
+#endif
+#endif /* not __BYTE_ORDER */
+
+#define FSYS_BUFLEN 0x8000
+#define FSYS_BUF fsys_buf
+
+/* This is the new super block of a journaling reiserfs system */
+struct reiserfs_super_block
+{
+ __u32 s_block_count; /* blocks count */
+ __u32 s_free_blocks; /* free blocks count */
+ __u32 s_root_block; /* root block number */
+ __u32 s_journal_block; /* journal block number */
+ __u32 s_journal_dev; /* journal device number */
+ __u32 s_journal_size; /* size of the journal on FS creation. used to make sure they don't overflow it */
+ __u32 s_journal_trans_max; /* max number of blocks in a transaction. */
+ __u32 s_journal_magic; /* random value made on fs creation */
+ __u32 s_journal_max_batch; /* max number of blocks to batch into a trans */
+ __u32 s_journal_max_commit_age; /* in seconds, how old can an async commit be */
+ __u32 s_journal_max_trans_age; /* in seconds, how old can a transaction be */
+ __u16 s_blocksize; /* block size */
+ __u16 s_oid_maxsize; /* max size of object id array */
+ __u16 s_oid_cursize; /* current size of object id array */
+ __u16 s_state; /* valid or error */
+ char s_magic[16]; /* reiserfs magic string indicates that file system is reiserfs */
+ __u16 s_tree_height; /* height of disk tree */
+ __u16 s_bmap_nr; /* amount of bitmap blocks needed to address each block of file system */
+ __u16 s_version;
+ char s_unused[128]; /* zero filled by mkreiserfs */
+};
+
+
+#define sb_root_block(sbp) (__le32_to_cpu((sbp)->s_root_block))
+#define sb_journal_block(sbp) (__le32_to_cpu((sbp)->s_journal_block))
+#define set_sb_journal_block(sbp,v) ((sbp)->s_journal_block = __cpu_to_le32(v))
+#define sb_journal_size(sbp) (__le32_to_cpu((sbp)->s_journal_size))
+#define sb_blocksize(sbp) (__le16_to_cpu((sbp)->s_blocksize))
+#define set_sb_blocksize(sbp,v) ((sbp)->s_blocksize = __cpu_to_le16(v))
+#define sb_version(sbp) (__le16_to_cpu((sbp)->s_version))
+#define set_sb_version(sbp,v) ((sbp)->s_version = __cpu_to_le16(v))
+
+
+#define REISERFS_MAX_SUPPORTED_VERSION 2
+#define REISERFS_SUPER_MAGIC_STRING "ReIsErFs"
+#define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs"
+#define REISER3FS_SUPER_MAGIC_STRING "ReIsEr3Fs"
+
+#define MAX_HEIGHT 7
+
+/* must be correct to keep the desc and commit structs at 4k */
+#define JOURNAL_TRANS_HALF 1018
+
+/* first block written in a commit. */
+struct reiserfs_journal_desc {
+ __u32 j_trans_id; /* id of commit */
+ __u32 j_len; /* length of commit. len +1 is the commit block */
+ __u32 j_mount_id; /* mount id of this trans*/
+ __u32 j_realblock[JOURNAL_TRANS_HALF]; /* real locations for the first blocks */
+ char j_magic[12];
+};
+
+/* last block written in a commit */
+struct reiserfs_journal_commit {
+ __u32 j_trans_id; /* must match j_trans_id from the desc block */
+ __u32 j_len; /* ditto */
+ __u32 j_realblock[JOURNAL_TRANS_HALF]; /* real locations for the last blocks */
+ char j_digest[16]; /* md5 sum of all the blocks involved, including desc and commit. not used, kill it */
+};
+
+/* this header block gets written whenever a transaction is considered
+ fully flushed, and is more recent than the last fully flushed
+ transaction.
+ fully flushed means all the log blocks and all the real blocks are
+ on disk, and this transaction does not need to be replayed.
+*/
+struct reiserfs_journal_header {
+ /* id of last fully flushed transaction */
+ __u32 j_last_flush_trans_id;
+ /* offset in the log of where to start replay after a crash */
+ __u32 j_first_unflushed_offset;
+ /* mount id to detect very old transactions */
+ __u32 j_mount_id;
+};
+
+/* magic string to find desc blocks in the journal */
+#define JOURNAL_DESC_MAGIC "ReIsErLB"
+
+
+/*
+ * directories use this key as well as old files
+ */
+struct offset_v1
+{
+ /*
+ * for regular files this is the offset to the first byte of the
+ * body, contained in the object-item, as measured from the start of
+ * the entire body of the object.
+ *
+ * for directory entries, k_offset consists of hash derived from
+ * hashing the name and using few bits (23 or more) of the resulting
+ * hash, and generation number that allows distinguishing names with
+ * hash collisions. If number of collisions overflows generation
+ * number, we return EEXIST. High order bit is 0 always
+ */
+ __u32 k_offset;
+ __u32 k_uniqueness;
+};
+
+struct offset_v2 {
+ /*
+ * for regular files this is the offset to the first byte of the
+ * body, contained in the object-item, as measured from the start of
+ * the entire body of the object.
+ *
+ * for directory entries, k_offset consists of hash derived from
+ * hashing the name and using few bits (23 or more) of the resulting
+ * hash, and generation number that allows distinguishing names with
+ * hash collisions. If number of collisions overflows generation
+ * number, we return EEXIST. High order bit is 0 always
+ */
+
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ /* little endian version */
+ __u64 k_offset:60;
+ __u64 k_type: 4;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ /* big endian version */
+ __u64 k_type: 4;
+ __u64 k_offset:60;
+#else
+#error "__LITTLE_ENDIAN_BITFIELD or __BIG_ENDIAN_BITFIELD must be defined"
+#endif
+} __attribute__ ((__packed__));
+
+#define TYPE_MAXTYPE 3
+#define TYPE_ANY 15
+
+#if (__BYTE_ORDER == __BIG_ENDIAN)
+typedef union {
+ struct offset_v2 offset_v2;
+ __u64 linear;
+} __attribute__ ((__packed__)) offset_v2_esafe_overlay;
+
+static inline __u16 offset_v2_k_type( const struct offset_v2 *v2 )
+{
+ offset_v2_esafe_overlay tmp = *(const offset_v2_esafe_overlay *)v2;
+ tmp.linear = __le64_to_cpu( tmp.linear );
+ return (tmp.offset_v2.k_type <= TYPE_MAXTYPE)?tmp.offset_v2.k_type:TYPE_ANY;
+}
+
+static inline loff_t offset_v2_k_offset( const struct offset_v2 *v2 )
+{
+ offset_v2_esafe_overlay tmp = *(const offset_v2_esafe_overlay *)v2;
+ tmp.linear = __le64_to_cpu( tmp.linear );
+ return tmp.offset_v2.k_offset;
+}
+#elif (__BYTE_ORDER == __LITTLE_ENDIAN)
+# define offset_v2_k_type(v2) ((v2)->k_type)
+# define offset_v2_k_offset(v2) ((v2)->k_offset)
+#else
+#error "__BYTE_ORDER must be __LITTLE_ENDIAN or __BIG_ENDIAN"
+#endif
+
+struct key
+{
+ /* packing locality: by default parent directory object id */
+ __u32 k_dir_id;
+ /* object identifier */
+ __u32 k_objectid;
+ /* the offset and node type (old and new form) */
+ union
+ {
+ struct offset_v1 v1;
+ struct offset_v2 v2;
+ }
+ u;
+};
+
+#define KEY_SIZE (sizeof (struct key))
+
+/* Header of a disk block. More precisely, header of a formatted leaf
+ or internal node, and not the header of an unformatted node. */
+struct block_head
+{
+ __u16 blk_level; /* Level of a block in the tree. */
+ __u16 blk_nr_item; /* Number of keys/items in a block. */
+ __u16 blk_free_space; /* Block free space in bytes. */
+ struct key blk_right_delim_key; /* Right delimiting key for this block (supported for leaf level nodes
+ only) */
+};
+#define BLKH_SIZE (sizeof (struct block_head))
+#define DISK_LEAF_NODE_LEVEL 1 /* Leaf node level. */
+
+struct item_head
+{
+ /* Everything in the tree is found by searching for it based on
+ * its key.*/
+ struct key ih_key;
+ union {
+ /* The free space in the last unformatted node of an
+ indirect item if this is an indirect item. This
+ equals 0xFFFF iff this is a direct item or stat data
+ item. Note that the key, not this field, is used to
+ determine the item type, and thus which field this
+ union contains. */
+ __u16 ih_free_space;
+ /* Iff this is a directory item, this field equals the
+ number of directory entries in the directory item. */
+ __u16 ih_entry_count;
+ } __attribute__ ((__packed__)) u;
+ __u16 ih_item_len; /* total size of the item body */
+ __u16 ih_item_location; /* an offset to the item body
+ * within the block */
+ __u16 ih_version; /* 0 for all old items, 2 for new
+ ones. Highest bit is set by fsck
+ temporary, cleaned after all
+ done */
+} __attribute__ ((__packed__));
+
+/* size of item header */
+#define IH_SIZE (sizeof (struct item_head))
+
+#define ITEM_VERSION_1 0
+#define ITEM_VERSION_2 1
+
+#define ih_version(ih) (__le16_to_cpu((ih)->ih_version))
+
+#define IH_KEY_OFFSET(ih) (ih_version(ih) == ITEM_VERSION_1 \
+ ? __le32_to_cpu((ih)->ih_key.u.v1.k_offset) \
+ : offset_v2_k_offset(&((ih)->ih_key.u.v2)))
+
+#define IH_KEY_ISTYPE(ih, type) (ih_version(ih) == ITEM_VERSION_1 \
+ ? __le32_to_cpu((ih)->ih_key.u.v1.k_uniqueness) == V1_##type \
+ : offset_v2_k_type(&((ih)->ih_key.u.v2)) == V2_##type)
+
+/***************************************************************************/
+/* DISK CHILD */
+/***************************************************************************/
+/* Disk child pointer: The pointer from an internal node of the tree
+ to a node that is on disk. */
+struct disk_child {
+ __u32 dc_block_number; /* Disk child's block number. */
+ __u16 dc_size; /* Disk child's used space. */
+ __u16 dc_reserved;
+};
+
+#define DC_SIZE (sizeof(struct disk_child))
+#define dc_block_number(dc_p) (__le32_to_cpu((dc_p)->dc_block_number))
+
+
+/*
+ * old stat data is 32 bytes long. We are going to distinguish new one by
+ * different size
+ */
+struct stat_data_v1
+{
+ __u16 sd_mode; /* file type, permissions */
+ __u16 sd_nlink; /* number of hard links */
+ __u16 sd_uid; /* owner */
+ __u16 sd_gid; /* group */
+ __u32 sd_size; /* file size */
+ __u32 sd_atime; /* time of last access */
+ __u32 sd_mtime; /* time file was last modified */
+ __u32 sd_ctime; /* time inode (stat data) was last changed (except changes to sd_atime and sd_mtime) */
+ union {
+ __u32 sd_rdev;
+ __u32 sd_blocks; /* number of blocks file uses */
+ } __attribute__ ((__packed__)) u;
+ __u32 sd_first_direct_byte; /* first byte of file which is stored
+ in a direct item: except that if it
+ equals 1 it is a symlink and if it
+ equals ~(__u32)0 there is no
+ direct item. The existence of this
+ field really grates on me. Let's
+ replace it with a macro based on
+ sd_size and our tail suppression
+ policy. Someday. -Hans */
+} __attribute__ ((__packed__));
+
+#define stat_data_v1(ih) (ih_version(ih) == ITEM_VERSION_1)
+#define sd_v1_mode(sdp) ((sdp)->sd_mode)
+#define sd_v1_nlink(sdp) (__le16_to_cpu((sdp)->sd_nlink))
+#define sd_v1_uid(sdp) (__le16_to_cpu((sdp)->sd_uid))
+#define sd_v1_gid(sdp) (__le16_to_cpu((sdp)->sd_gid))
+#define sd_v1_size(sdp) (__le32_to_cpu((sdp)->sd_size))
+#define sd_v1_mtime(sdp) (__le32_to_cpu((sdp)->sd_mtime))
+
+/* Stat Data on disk (reiserfs version of UFS disk inode minus the
+ address blocks) */
+struct stat_data {
+ __u16 sd_mode; /* file type, permissions */
+ __u16 sd_attrs; /* persistent inode flags */
+ __u32 sd_nlink; /* number of hard links */
+ __u64 sd_size; /* file size */
+ __u32 sd_uid; /* owner */
+ __u32 sd_gid; /* group */
+ __u32 sd_atime; /* time of last access */
+ __u32 sd_mtime; /* time file was last modified */
+ __u32 sd_ctime; /* time inode (stat data) was last changed (except changes to sd_atime and sd_mtime) */
+ __u32 sd_blocks;
+ union {
+ __u32 sd_rdev;
+ __u32 sd_generation;
+ /*__u32 sd_first_direct_byte; */
+ /* first byte of file which is stored in a
+ direct item: except that if it equals 1
+ it is a symlink and if it equals
+ ~(__u32)0 there is no direct item. The
+ existence of this field really grates
+ on me. Let's replace it with a macro
+ based on sd_size and our tail
+ suppression policy? */
+ } __attribute__ ((__packed__)) u;
+} __attribute__ ((__packed__));
+
+#define stat_data_v2(ih) (ih_version(ih) == ITEM_VERSION_2)
+#define sd_v2_mode(sdp) (__le16_to_cpu((sdp)->sd_mode))
+#define sd_v2_nlink(sdp) (__le32_to_cpu((sdp)->sd_nlink))
+#define sd_v2_size(sdp) (__le64_to_cpu((sdp)->sd_size))
+#define sd_v2_uid(sdp) (__le32_to_cpu((sdp)->sd_uid))
+#define sd_v2_gid(sdp) (__le32_to_cpu((sdp)->sd_gid))
+#define sd_v2_mtime(sdp) (__le32_to_cpu((sdp)->sd_mtime))
+
+#define sd_mode(sdp) (__le16_to_cpu((sdp)->sd_mode))
+#define sd_size(sdp) (__le32_to_cpu((sdp)->sd_size))
+#define sd_size_hi(sdp) (__le32_to_cpu((sdp)->sd_size_hi))
+
+struct reiserfs_de_head
+{
+ __u32 deh_offset; /* third component of the directory entry key */
+ __u32 deh_dir_id; /* objectid of the parent directory of the
+ object, that is referenced by directory entry */
+ __u32 deh_objectid;/* objectid of the object, that is referenced by
+ directory entry */
+ __u16 deh_location;/* offset of name in the whole item */
+ __u16 deh_state; /* whether 1) entry contains stat data (for
+ future), and 2) whether entry is hidden
+ (unlinked) */
+};
+
+#define DEH_SIZE (sizeof (struct reiserfs_de_head))
+#define deh_offset(p_deh) (__le32_to_cpu((p_deh)->deh_offset))
+#define deh_dir_id(p_deh) (__le32_to_cpu((p_deh)->deh_dir_id))
+#define deh_objectid(p_deh) (__le32_to_cpu((p_deh)->deh_objectid))
+#define deh_location(p_deh) (__le16_to_cpu((p_deh)->deh_location))
+#define deh_state(p_deh) (__le16_to_cpu((p_deh)->deh_state))
+
+
+#define DEH_Statdata (1 << 0) /* not used now */
+#define DEH_Visible (1 << 2)
+
+#define SD_OFFSET 0
+#define SD_UNIQUENESS 0
+#define DOT_OFFSET 1
+#define DOT_DOT_OFFSET 2
+#define DIRENTRY_UNIQUENESS 500
+
+#define V1_TYPE_STAT_DATA 0x0
+#define V1_TYPE_DIRECT 0xffffffff
+#define V1_TYPE_INDIRECT 0xfffffffe
+#define V1_TYPE_DIRECTORY_MAX 0xfffffffd
+#define V2_TYPE_STAT_DATA 0
+#define V2_TYPE_INDIRECT 1
+#define V2_TYPE_DIRECT 2
+#define V2_TYPE_DIRENTRY 3
+
+#define REISERFS_ROOT_OBJECTID 2
+#define REISERFS_ROOT_PARENT_OBJECTID 1
+#define REISERFS_DISK_OFFSET_IN_BYTES (64 * 1024)
+/* the spot for the super in versions 3.5 - 3.5.11 (inclusive) */
+#define REISERFS_OLD_DISK_OFFSET_IN_BYTES (8 * 1024)
+#define REISERFS_OLD_BLOCKSIZE 4096
+
+#define S_ISREG(mode) (((mode) & 0170000) == 0100000)
+#define S_ISDIR(mode) (((mode) & 0170000) == 0040000)
+#define S_ISLNK(mode) (((mode) & 0170000) == 0120000)
+
+#define PATH_MAX 1024 /* include/linux/limits.h */
+#define MAX_LINK_COUNT 5 /* number of symbolic links to follow */
+
+/* The size of the node cache */
+#define FSYSREISER_CACHE_SIZE 24*1024
+#define FSYSREISER_MIN_BLOCKSIZE SECTOR_SIZE
+#define FSYSREISER_MAX_BLOCKSIZE FSYSREISER_CACHE_SIZE / 3
+
+/* Info about currently opened file */
+struct fsys_reiser_fileinfo
+{
+ __u32 k_dir_id;
+ __u32 k_objectid;
+};
+
+/* In memory info about the currently mounted filesystem */
+struct fsys_reiser_info
+{
+ /* The last read item head */
+ struct item_head *current_ih;
+ /* The last read item */
+ char *current_item;
+ /* The information for the currently opened file */
+ struct fsys_reiser_fileinfo fileinfo;
+ /* The start of the journal */
+ __u32 journal_block;
+ /* The size of the journal */
+ __u32 journal_block_count;
+ /* The first valid descriptor block in journal
+ (relative to journal_block) */
+ __u32 journal_first_desc;
+
+ /* The ReiserFS version. */
+ __u16 version;
+ /* The current depth of the reiser tree. */
+ __u16 tree_depth;
+ /* SECTOR_SIZE << blocksize_shift == blocksize. */
+ __u8 blocksize_shift;
+ /* 1 << full_blocksize_shift == blocksize. */
+ __u8 fullblocksize_shift;
+ /* The reiserfs block size (must be a power of 2) */
+ __u16 blocksize;
+ /* The number of cached tree nodes */
+ __u16 cached_slots;
+ /* The number of valid transactions in journal */
+ __u16 journal_transactions;
+
+ unsigned int blocks[MAX_HEIGHT];
+ unsigned int next_key_nr[MAX_HEIGHT];
+};
+
+/* The cached s+tree blocks in FSYS_BUF, see below
+ * for a more detailed description.
+ */
+#define ROOT ((char *) ((int) FSYS_BUF))
+#define CACHE(i) (ROOT + ((i) << INFO->fullblocksize_shift))
+#define LEAF CACHE (DISK_LEAF_NODE_LEVEL)
+
+#define BLOCKHEAD(cache) ((struct block_head *) cache)
+#define ITEMHEAD ((struct item_head *) ((int) LEAF + BLKH_SIZE))
+#define KEY(cache) ((struct key *) ((int) cache + BLKH_SIZE))
+#define DC(cache) ((struct disk_child *) \
+ ((int) cache + BLKH_SIZE + KEY_SIZE * nr_item))
+/* The fsys_reiser_info block.
+ */
+#define INFO \
+ ((struct fsys_reiser_info *) ((int) FSYS_BUF + FSYSREISER_CACHE_SIZE))
+/*
+ * The journal cache. For each transaction it contains the number of
+ * blocks followed by the real block numbers of this transaction.
+ *
+ * If the block numbers of some transaction won't fit in this space,
+ * this list is stopped with a 0xffffffff marker and the remaining
+ * uncommitted transactions aren't cached.
+ */
+#define JOURNAL_START ((__u32 *) (INFO + 1))
+#define JOURNAL_END ((__u32 *) (FSYS_BUF + FSYS_BUFLEN))
+
+
+static __inline__ unsigned long
+log2 (unsigned long word)
+{
+#ifdef __I386__
+ __asm__ ("bsfl %1,%0"
+ : "=r" (word)
+ : "r" (word));
+ return word;
+#else
+ int i;
+
+ for(i=0; i<(8*sizeof(word)); i++)
+ if ((1<<i) & word)
+ return i;
+
+ return 0;
+#endif
+}
+
+static __inline__ int
+is_power_of_two (unsigned long word)
+{
+ return (word & -word) == word;
+}
+
+extern const char *bb_mode_string(int mode);
+extern int reiserfs_devread (int sector, int byte_offset, int byte_len, char *buf);
diff --git a/roms/u-boot/fs/sandbox/Makefile b/roms/u-boot/fs/sandbox/Makefile
new file mode 100644
index 000000000..06090519b
--- /dev/null
+++ b/roms/u-boot/fs/sandbox/Makefile
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (c) 2012, Google Inc.
+#
+# (C) Copyright 2006
+# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+#
+# (C) Copyright 2003
+# Pavel Bartusek, Sysgo Real-Time Solutions AG, pba@sysgo.de
+
+obj-y := sandboxfs.o
diff --git a/roms/u-boot/fs/sandbox/sandboxfs.c b/roms/u-boot/fs/sandbox/sandboxfs.c
new file mode 100644
index 000000000..4ae41d5b4
--- /dev/null
+++ b/roms/u-boot/fs/sandbox/sandboxfs.c
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2012, Google Inc.
+ */
+
+#include <common.h>
+#include <fs.h>
+#include <malloc.h>
+#include <os.h>
+#include <sandboxfs.h>
+
+int sandbox_fs_set_blk_dev(struct blk_desc *rbdd, struct disk_partition *info)
+{
+ /*
+ * Only accept a NULL struct blk_desc for the sandbox, which is when
+ * hostfs interface is used
+ */
+ return rbdd != NULL;
+}
+
+int sandbox_fs_read_at(const char *filename, loff_t pos, void *buffer,
+ loff_t maxsize, loff_t *actread)
+{
+ loff_t size;
+ int fd, ret;
+
+ fd = os_open(filename, OS_O_RDONLY);
+ if (fd < 0)
+ return fd;
+ ret = os_lseek(fd, pos, OS_SEEK_SET);
+ if (ret == -1) {
+ os_close(fd);
+ return ret;
+ }
+ if (!maxsize) {
+ ret = os_get_filesize(filename, &size);
+ if (ret) {
+ os_close(fd);
+ return ret;
+ }
+
+ maxsize = size;
+ }
+
+ size = os_read(fd, buffer, maxsize);
+ os_close(fd);
+
+ if (size < 0) {
+ ret = -1;
+ } else {
+ ret = 0;
+ *actread = size;
+ }
+
+ return ret;
+}
+
+int sandbox_fs_write_at(const char *filename, loff_t pos, void *buffer,
+ loff_t towrite, loff_t *actwrite)
+{
+ ssize_t size;
+ int fd, ret;
+
+ fd = os_open(filename, OS_O_RDWR | OS_O_CREAT);
+ if (fd < 0)
+ return fd;
+ ret = os_lseek(fd, pos, OS_SEEK_SET);
+ if (ret == -1) {
+ os_close(fd);
+ return ret;
+ }
+ size = os_write(fd, buffer, towrite);
+ os_close(fd);
+
+ if (size == -1) {
+ ret = -1;
+ } else {
+ ret = 0;
+ *actwrite = size;
+ }
+
+ return ret;
+}
+
+int sandbox_fs_ls(const char *dirname)
+{
+ struct os_dirent_node *head, *node;
+ int ret;
+
+ ret = os_dirent_ls(dirname, &head);
+ if (ret)
+ goto out;
+
+ for (node = head; node; node = node->next) {
+ printf("%s %10lu %s\n", os_dirent_get_typename(node->type),
+ node->size, node->name);
+ }
+out:
+ os_dirent_free(head);
+
+ return ret;
+}
+
+int sandbox_fs_exists(const char *filename)
+{
+ loff_t size;
+ int ret;
+
+ ret = os_get_filesize(filename, &size);
+ return ret == 0;
+}
+
+int sandbox_fs_size(const char *filename, loff_t *size)
+{
+ return os_get_filesize(filename, size);
+}
+
+void sandbox_fs_close(void)
+{
+}
+
+int fs_read_sandbox(const char *filename, void *buf, loff_t offset, loff_t len,
+ loff_t *actread)
+{
+ int ret;
+
+ ret = sandbox_fs_read_at(filename, offset, buf, len, actread);
+ if (ret)
+ printf("** Unable to read file %s **\n", filename);
+
+ return ret;
+}
+
+int fs_write_sandbox(const char *filename, void *buf, loff_t offset,
+ loff_t len, loff_t *actwrite)
+{
+ int ret;
+
+ ret = sandbox_fs_write_at(filename, offset, buf, len, actwrite);
+ if (ret)
+ printf("** Unable to write file %s **\n", filename);
+
+ return ret;
+}
diff --git a/roms/u-boot/fs/squashfs/Kconfig b/roms/u-boot/fs/squashfs/Kconfig
new file mode 100644
index 000000000..54ab1618f
--- /dev/null
+++ b/roms/u-boot/fs/squashfs/Kconfig
@@ -0,0 +1,11 @@
+config FS_SQUASHFS
+ bool "Enable SquashFS filesystem support"
+ select ZLIB_UNCOMPRESS
+ help
+ This provides support for reading images from SquashFS filesystem.
+ Squashfs is a compressed read-only filesystem for Linux.
+ It uses zlib, lz4, lzo, or xz compression to compress files, inodes
+ and directories. Squashfs is intended for general read-only
+ filesystem use, for archival use (i.e. in cases where a .tar.gz file
+ may be used), and in constrained block device/memory systems (e.g.
+ embedded systems) where low overhead is needed.
diff --git a/roms/u-boot/fs/squashfs/Makefile b/roms/u-boot/fs/squashfs/Makefile
new file mode 100644
index 000000000..ba66ee821
--- /dev/null
+++ b/roms/u-boot/fs/squashfs/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+
+obj-$(CONFIG_$(SPL_)FS_SQUASHFS) = sqfs.o \
+ sqfs_inode.o \
+ sqfs_dir.o \
+ sqfs_decompressor.o
diff --git a/roms/u-boot/fs/squashfs/sqfs.c b/roms/u-boot/fs/squashfs/sqfs.c
new file mode 100644
index 000000000..92ab8ac63
--- /dev/null
+++ b/roms/u-boot/fs/squashfs/sqfs.c
@@ -0,0 +1,1723 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Bootlin
+ *
+ * Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
+ *
+ * sqfs.c: SquashFS filesystem implementation
+ */
+
+#include <asm/unaligned.h>
+#include <errno.h>
+#include <fs.h>
+#include <linux/types.h>
+#include <linux/byteorder/little_endian.h>
+#include <linux/byteorder/generic.h>
+#include <memalign.h>
+#include <stdlib.h>
+#include <string.h>
+#include <squashfs.h>
+#include <part.h>
+
+#include "sqfs_decompressor.h"
+#include "sqfs_filesystem.h"
+#include "sqfs_utils.h"
+
+static struct squashfs_ctxt ctxt;
+
+static int sqfs_disk_read(__u32 block, __u32 nr_blocks, void *buf)
+{
+ ulong ret;
+
+ if (!ctxt.cur_dev)
+ return -1;
+
+ ret = blk_dread(ctxt.cur_dev, ctxt.cur_part_info.start + block,
+ nr_blocks, buf);
+
+ if (ret != nr_blocks)
+ return -1;
+
+ return ret;
+}
+
+static int sqfs_read_sblk(struct squashfs_super_block **sblk)
+{
+ *sblk = malloc_cache_aligned(ctxt.cur_dev->blksz);
+ if (!*sblk)
+ return -ENOMEM;
+
+ if (sqfs_disk_read(0, 1, *sblk) != 1) {
+ free(*sblk);
+ sblk = NULL;
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sqfs_count_tokens(const char *filename)
+{
+ int token_count = 1, l;
+
+ for (l = 1; l < strlen(filename); l++) {
+ if (filename[l] == '/')
+ token_count++;
+ }
+
+ /* Ignore trailing '/' in path */
+ if (filename[strlen(filename) - 1] == '/')
+ token_count--;
+
+ if (!token_count)
+ token_count = 1;
+
+ return token_count;
+}
+
+/*
+ * Calculates how many blocks are needed for the buffer used in sqfs_disk_read.
+ * The memory section (e.g. inode table) start offset and its end (i.e. the next
+ * table start) must be specified. It also calculates the offset from which to
+ * start reading the buffer.
+ */
+static int sqfs_calc_n_blks(__le64 start, __le64 end, u64 *offset)
+{
+ u64 start_, table_size;
+
+ table_size = le64_to_cpu(end) - le64_to_cpu(start);
+ start_ = le64_to_cpu(start) / ctxt.cur_dev->blksz;
+ *offset = le64_to_cpu(start) - (start_ * ctxt.cur_dev->blksz);
+
+ return DIV_ROUND_UP(table_size + *offset, ctxt.cur_dev->blksz);
+}
+
+/*
+ * Retrieves fragment block entry and returns true if the fragment block is
+ * compressed
+ */
+static int sqfs_frag_lookup(u32 inode_fragment_index,
+ struct squashfs_fragment_block_entry *e)
+{
+ u64 start, n_blks, src_len, table_offset, start_block;
+ unsigned char *metadata_buffer, *metadata, *table;
+ struct squashfs_fragment_block_entry *entries;
+ struct squashfs_super_block *sblk = ctxt.sblk;
+ unsigned long dest_len;
+ int block, offset, ret;
+ u16 header;
+
+ metadata_buffer = NULL;
+ entries = NULL;
+ table = NULL;
+
+ if (inode_fragment_index >= get_unaligned_le32(&sblk->fragments))
+ return -EINVAL;
+
+ start = get_unaligned_le64(&sblk->fragment_table_start) /
+ ctxt.cur_dev->blksz;
+ n_blks = sqfs_calc_n_blks(sblk->fragment_table_start,
+ sblk->export_table_start,
+ &table_offset);
+
+ /* Allocate a proper sized buffer to store the fragment index table */
+ table = malloc_cache_aligned(n_blks * ctxt.cur_dev->blksz);
+ if (!table) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (sqfs_disk_read(start, n_blks, table) < 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ block = SQFS_FRAGMENT_INDEX(inode_fragment_index);
+ offset = SQFS_FRAGMENT_INDEX_OFFSET(inode_fragment_index);
+
+ /*
+ * Get the start offset of the metadata block that contains the right
+ * fragment block entry
+ */
+ start_block = get_unaligned_le64(table + table_offset + block *
+ sizeof(u64));
+
+ start = start_block / ctxt.cur_dev->blksz;
+ n_blks = sqfs_calc_n_blks(cpu_to_le64(start_block),
+ sblk->fragment_table_start, &table_offset);
+
+ metadata_buffer = malloc_cache_aligned(n_blks * ctxt.cur_dev->blksz);
+ if (!metadata_buffer) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (sqfs_disk_read(start, n_blks, metadata_buffer) < 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Every metadata block starts with a 16-bit header */
+ header = get_unaligned_le16(metadata_buffer + table_offset);
+ metadata = metadata_buffer + table_offset + SQFS_HEADER_SIZE;
+
+ if (!metadata || !header) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ entries = malloc(SQFS_METADATA_BLOCK_SIZE);
+ if (!entries) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (SQFS_COMPRESSED_METADATA(header)) {
+ src_len = SQFS_METADATA_SIZE(header);
+ dest_len = SQFS_METADATA_BLOCK_SIZE;
+ ret = sqfs_decompress(&ctxt, entries, &dest_len, metadata,
+ src_len);
+ if (ret) {
+ ret = -EINVAL;
+ goto out;
+ }
+ } else {
+ memcpy(entries, metadata, SQFS_METADATA_SIZE(header));
+ }
+
+ *e = entries[offset];
+ ret = SQFS_COMPRESSED_BLOCK(e->size);
+
+out:
+ free(entries);
+ free(metadata_buffer);
+ free(table);
+
+ return ret;
+}
+
+/*
+ * The entry name is a flexible array member, and we don't know its size before
+ * actually reading the entry. So we need a first copy to retrieve this size so
+ * we can finally copy the whole struct.
+ */
+static int sqfs_read_entry(struct squashfs_directory_entry **dest, void *src)
+{
+ struct squashfs_directory_entry *tmp;
+ u16 sz;
+
+ tmp = src;
+ sz = get_unaligned_le16(src + sizeof(*tmp) - sizeof(u16));
+ /*
+ * 'src' points to the begin of a directory entry, and 'sz' gets its
+ * 'name_size' member's value. name_size is actually the string
+ * length - 1, so adding 2 compensates this difference and adds space
+ * for the trailling null byte.
+ */
+ *dest = malloc(sizeof(*tmp) + sz + 2);
+ if (!*dest)
+ return -ENOMEM;
+
+ memcpy(*dest, src, sizeof(*tmp) + sz + 1);
+ (*dest)->name[sz + 1] = '\0';
+
+ return 0;
+}
+
+static int sqfs_get_tokens_length(char **tokens, int count)
+{
+ int length = 0, i;
+
+ /*
+ * 1 is added to the result of strlen to consider the slash separator
+ * between the tokens.
+ */
+ for (i = 0; i < count; i++)
+ length += strlen(tokens[i]) + 1;
+
+ return length;
+}
+
+/* Takes a token list and returns a single string with '/' as separator. */
+static char *sqfs_concat_tokens(char **token_list, int token_count)
+{
+ char *result;
+ int i, length = 0, offset = 0;
+
+ length = sqfs_get_tokens_length(token_list, token_count);
+
+ result = malloc(length + 1);
+ if (!result)
+ return NULL;
+
+ result[length] = '\0';
+
+ for (i = 0; i < token_count; i++) {
+ strcpy(result + offset, token_list[i]);
+ offset += strlen(token_list[i]);
+ result[offset++] = '/';
+ }
+
+ return result;
+}
+
+/*
+ * Differently from sqfs_concat_tokens, sqfs_join writes the result into a
+ * previously allocated string, and returns the number of bytes written.
+ */
+static int sqfs_join(char **strings, char *dest, int start, int end,
+ char separator)
+{
+ int i, offset = 0;
+
+ for (i = start; i < end; i++) {
+ strcpy(dest + offset, strings[i]);
+ offset += strlen(strings[i]);
+ if (i < end - 1)
+ dest[offset++] = separator;
+ }
+
+ return offset;
+}
+
+/*
+ * Fills the given token list using its size (count) and a source string (str)
+ */
+static int sqfs_tokenize(char **tokens, int count, const char *str)
+{
+ int i, j, ret = 0;
+ char *aux, *strc;
+
+ strc = strdup(str);
+ if (!strc)
+ return -ENOMEM;
+
+ if (!strcmp(strc, "/")) {
+ tokens[0] = strdup(strc);
+ if (!tokens[0]) {
+ ret = -ENOMEM;
+ goto free_strc;
+ }
+ } else {
+ for (j = 0; j < count; j++) {
+ aux = strtok(!j ? strc : NULL, "/");
+ tokens[j] = strdup(aux);
+ if (!tokens[j]) {
+ for (i = 0; i < j; i++)
+ free(tokens[i]);
+ ret = -ENOMEM;
+ goto free_strc;
+ }
+ }
+ }
+
+free_strc:
+ free(strc);
+
+ return ret;
+}
+
+/*
+ * Remove last 'updir + 1' tokens from the base path tokens list. This leaves us
+ * with a token list containing only the tokens needed to form the resolved
+ * path, and returns the decremented size of the token list.
+ */
+static int sqfs_clean_base_path(char **base, int count, int updir)
+{
+ int i;
+
+ for (i = count - updir - 1; i < count; i++)
+ free(base[i]);
+
+ return count - updir - 1;
+}
+
+/*
+ * Given the base ("current dir.") path and the relative one, generate the
+ * absolute path.
+ */
+static char *sqfs_get_abs_path(const char *base, const char *rel)
+{
+ char **base_tokens, **rel_tokens, *resolved = NULL;
+ int ret, bc, rc, i, updir = 0, resolved_size = 0, offset = 0;
+
+ base_tokens = NULL;
+ rel_tokens = NULL;
+
+ /* Memory allocation for the token lists */
+ bc = sqfs_count_tokens(base);
+ rc = sqfs_count_tokens(rel);
+ if (bc < 1 || rc < 1)
+ return NULL;
+
+ base_tokens = calloc(bc, sizeof(char *));
+ if (!base_tokens)
+ return NULL;
+
+ rel_tokens = calloc(rc, sizeof(char *));
+ if (!rel_tokens)
+ goto out;
+
+ /* Fill token lists */
+ ret = sqfs_tokenize(base_tokens, bc, base);
+ if (ret)
+ goto out;
+
+ ret = sqfs_tokenize(rel_tokens, rc, rel);
+ if (ret)
+ goto out;
+
+ /* count '..' occurrences in target path */
+ for (i = 0; i < rc; i++) {
+ if (!strcmp(rel_tokens[i], ".."))
+ updir++;
+ }
+
+ /* Remove the last token and the '..' occurrences */
+ bc = sqfs_clean_base_path(base_tokens, bc, updir);
+ if (bc < 0)
+ goto out;
+
+ /* Calculate resolved path size */
+ if (!bc)
+ resolved_size++;
+
+ resolved_size += sqfs_get_tokens_length(base_tokens, bc) +
+ sqfs_get_tokens_length(rel_tokens, rc);
+
+ resolved = malloc(resolved_size + 1);
+ if (!resolved)
+ goto out;
+
+ /* Set resolved path */
+ memset(resolved, '\0', resolved_size + 1);
+ offset += sqfs_join(base_tokens, resolved + offset, 0, bc, '/');
+ resolved[offset++] = '/';
+ offset += sqfs_join(rel_tokens, resolved + offset, updir, rc, '/');
+
+out:
+ if (rel_tokens)
+ for (i = 0; i < rc; i++)
+ free(rel_tokens[i]);
+ if (base_tokens)
+ for (i = 0; i < bc; i++)
+ free(base_tokens[i]);
+
+ free(rel_tokens);
+ free(base_tokens);
+
+ return resolved;
+}
+
+static char *sqfs_resolve_symlink(struct squashfs_symlink_inode *sym,
+ const char *base_path)
+{
+ char *resolved, *target;
+ u32 sz;
+
+ sz = get_unaligned_le32(&sym->symlink_size);
+ target = malloc(sz + 1);
+ if (!target)
+ return NULL;
+
+ /*
+ * There is no trailling null byte in the symlink's target path, so a
+ * copy is made and a '\0' is added at its end.
+ */
+ target[sz] = '\0';
+ /* Get target name (relative path) */
+ strncpy(target, sym->symlink, sz);
+
+ /* Relative -> absolute path conversion */
+ resolved = sqfs_get_abs_path(base_path, target);
+
+ free(target);
+
+ return resolved;
+}
+
+/*
+ * m_list contains each metadata block's position, and m_count is the number of
+ * elements of m_list. Those metadata blocks come from the compressed directory
+ * table.
+ */
+static int sqfs_search_dir(struct squashfs_dir_stream *dirs, char **token_list,
+ int token_count, u32 *m_list, int m_count)
+{
+ struct squashfs_super_block *sblk = ctxt.sblk;
+ char *path, *target, **sym_tokens, *res, *rem;
+ int j, ret = 0, new_inode_number, offset;
+ struct squashfs_symlink_inode *sym;
+ struct squashfs_ldir_inode *ldir;
+ struct squashfs_dir_inode *dir;
+ struct fs_dir_stream *dirsp;
+ struct fs_dirent *dent;
+ unsigned char *table;
+
+ res = NULL;
+ rem = NULL;
+ path = NULL;
+ target = NULL;
+ sym_tokens = NULL;
+
+ dirsp = (struct fs_dir_stream *)dirs;
+
+ /* Start by root inode */
+ table = sqfs_find_inode(dirs->inode_table, le32_to_cpu(sblk->inodes),
+ sblk->inodes, sblk->block_size);
+
+ dir = (struct squashfs_dir_inode *)table;
+ ldir = (struct squashfs_ldir_inode *)table;
+
+ /* get directory offset in directory table */
+ offset = sqfs_dir_offset(table, m_list, m_count);
+ dirs->table = &dirs->dir_table[offset];
+
+ /* Setup directory header */
+ dirs->dir_header = malloc(SQFS_DIR_HEADER_SIZE);
+ if (!dirs->dir_header)
+ return -ENOMEM;
+
+ memcpy(dirs->dir_header, dirs->table, SQFS_DIR_HEADER_SIZE);
+
+ /* Initialize squashfs_dir_stream members */
+ dirs->table += SQFS_DIR_HEADER_SIZE;
+ dirs->size = get_unaligned_le16(&dir->file_size) - SQFS_DIR_HEADER_SIZE;
+ dirs->entry_count = dirs->dir_header->count + 1;
+
+ /* No path given -> root directory */
+ if (!strcmp(token_list[0], "/")) {
+ dirs->table = &dirs->dir_table[offset];
+ memcpy(&dirs->i_dir, dir, sizeof(*dir));
+ return 0;
+ }
+
+ for (j = 0; j < token_count; j++) {
+ if (!sqfs_is_dir(get_unaligned_le16(&dir->inode_type))) {
+ printf("** Cannot find directory. **\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ while (!sqfs_readdir(dirsp, &dent)) {
+ ret = strcmp(dent->name, token_list[j]);
+ if (!ret)
+ break;
+ free(dirs->entry);
+ dirs->entry = NULL;
+ }
+
+ if (ret) {
+ printf("** Cannot find directory. **\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Redefine inode as the found token */
+ new_inode_number = dirs->entry->inode_offset +
+ dirs->dir_header->inode_number;
+
+ /* Get reference to inode in the inode table */
+ table = sqfs_find_inode(dirs->inode_table, new_inode_number,
+ sblk->inodes, sblk->block_size);
+ dir = (struct squashfs_dir_inode *)table;
+
+ /* Check for symbolic link and inode type sanity */
+ if (get_unaligned_le16(&dir->inode_type) == SQFS_SYMLINK_TYPE) {
+ sym = (struct squashfs_symlink_inode *)table;
+ /* Get first j + 1 tokens */
+ path = sqfs_concat_tokens(token_list, j + 1);
+ if (!path) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ /* Resolve for these tokens */
+ target = sqfs_resolve_symlink(sym, path);
+ if (!target) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ /* Join remaining tokens */
+ rem = sqfs_concat_tokens(token_list + j + 1, token_count -
+ j - 1);
+ if (!rem) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ /* Concatenate remaining tokens and symlink's target */
+ res = malloc(strlen(rem) + strlen(target) + 1);
+ if (!res) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ strcpy(res, target);
+ res[strlen(target)] = '/';
+ strcpy(res + strlen(target) + 1, rem);
+ token_count = sqfs_count_tokens(res);
+
+ if (token_count < 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ sym_tokens = malloc(token_count * sizeof(char *));
+ if (!sym_tokens) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Fill tokens list */
+ ret = sqfs_tokenize(sym_tokens, token_count, res);
+ if (ret) {
+ ret = -EINVAL;
+ goto out;
+ }
+ free(dirs->entry);
+ dirs->entry = NULL;
+
+ ret = sqfs_search_dir(dirs, sym_tokens, token_count,
+ m_list, m_count);
+ goto out;
+ } else if (!sqfs_is_dir(get_unaligned_le16(&dir->inode_type))) {
+ printf("** Cannot find directory. **\n");
+ free(dirs->entry);
+ dirs->entry = NULL;
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Check if it is an extended dir. */
+ if (get_unaligned_le16(&dir->inode_type) == SQFS_LDIR_TYPE)
+ ldir = (struct squashfs_ldir_inode *)table;
+
+ /* Get dir. offset into the directory table */
+ offset = sqfs_dir_offset(table, m_list, m_count);
+ dirs->table = &dirs->dir_table[offset];
+
+ /* Copy directory header */
+ memcpy(dirs->dir_header, &dirs->dir_table[offset],
+ SQFS_DIR_HEADER_SIZE);
+
+ /* Check for empty directory */
+ if (sqfs_is_empty_dir(table)) {
+ printf("Empty directory.\n");
+ free(dirs->entry);
+ dirs->entry = NULL;
+ ret = SQFS_EMPTY_DIR;
+ goto out;
+ }
+
+ dirs->table += SQFS_DIR_HEADER_SIZE;
+ dirs->size = get_unaligned_le16(&dir->file_size);
+ dirs->entry_count = dirs->dir_header->count + 1;
+ dirs->size -= SQFS_DIR_HEADER_SIZE;
+ free(dirs->entry);
+ dirs->entry = NULL;
+ }
+
+ offset = sqfs_dir_offset(table, m_list, m_count);
+ dirs->table = &dirs->dir_table[offset];
+
+ if (get_unaligned_le16(&dir->inode_type) == SQFS_DIR_TYPE)
+ memcpy(&dirs->i_dir, dir, sizeof(*dir));
+ else
+ memcpy(&dirs->i_ldir, ldir, sizeof(*ldir));
+
+out:
+ free(res);
+ free(rem);
+ free(path);
+ free(target);
+ free(sym_tokens);
+ return ret;
+}
+
+/*
+ * Inode and directory tables are stored as a series of metadata blocks, and
+ * given the compressed size of this table, we can calculate how much metadata
+ * blocks are needed to store the result of the decompression, since a
+ * decompressed metadata block should have a size of 8KiB.
+ */
+static int sqfs_count_metablks(void *table, u32 offset, int table_size)
+{
+ int count = 0, cur_size = 0, ret;
+ u32 data_size;
+ bool comp;
+
+ do {
+ ret = sqfs_read_metablock(table, offset + cur_size, &comp,
+ &data_size);
+ if (ret)
+ return -EINVAL;
+ cur_size += data_size + SQFS_HEADER_SIZE;
+ count++;
+ } while (cur_size < table_size);
+
+ return count;
+}
+
+/*
+ * Storing the metadata blocks header's positions will be useful while looking
+ * for an entry in the directory table, using the reference (index and offset)
+ * given by its inode.
+ */
+static int sqfs_get_metablk_pos(u32 *pos_list, void *table, u32 offset,
+ int metablks_count)
+{
+ u32 data_size, cur_size = 0;
+ int j, ret = 0;
+ bool comp;
+
+ if (!metablks_count)
+ return -EINVAL;
+
+ for (j = 0; j < metablks_count; j++) {
+ ret = sqfs_read_metablock(table, offset + cur_size, &comp,
+ &data_size);
+ if (ret)
+ return -EINVAL;
+
+ cur_size += data_size + SQFS_HEADER_SIZE;
+ pos_list[j] = cur_size;
+ }
+
+ return ret;
+}
+
+static int sqfs_read_inode_table(unsigned char **inode_table)
+{
+ struct squashfs_super_block *sblk = ctxt.sblk;
+ u64 start, n_blks, table_offset, table_size;
+ int j, ret = 0, metablks_count;
+ unsigned char *src_table, *itb;
+ u32 src_len, dest_offset = 0;
+ unsigned long dest_len = 0;
+ bool compressed;
+
+ table_size = get_unaligned_le64(&sblk->directory_table_start) -
+ get_unaligned_le64(&sblk->inode_table_start);
+ start = get_unaligned_le64(&sblk->inode_table_start) /
+ ctxt.cur_dev->blksz;
+ n_blks = sqfs_calc_n_blks(sblk->inode_table_start,
+ sblk->directory_table_start, &table_offset);
+
+ /* Allocate a proper sized buffer (itb) to store the inode table */
+ itb = malloc_cache_aligned(n_blks * ctxt.cur_dev->blksz);
+ if (!itb)
+ return -ENOMEM;
+
+ if (sqfs_disk_read(start, n_blks, itb) < 0) {
+ ret = -EINVAL;
+ goto free_itb;
+ }
+
+ /* Parse inode table (metadata block) header */
+ ret = sqfs_read_metablock(itb, table_offset, &compressed, &src_len);
+ if (ret) {
+ ret = -EINVAL;
+ goto free_itb;
+ }
+
+ /* Calculate size to store the whole decompressed table */
+ metablks_count = sqfs_count_metablks(itb, table_offset, table_size);
+ if (metablks_count < 1) {
+ ret = -EINVAL;
+ goto free_itb;
+ }
+
+ *inode_table = malloc(metablks_count * SQFS_METADATA_BLOCK_SIZE);
+ if (!*inode_table) {
+ ret = -ENOMEM;
+ goto free_itb;
+ }
+
+ src_table = itb + table_offset + SQFS_HEADER_SIZE;
+
+ /* Extract compressed Inode table */
+ for (j = 0; j < metablks_count; j++) {
+ sqfs_read_metablock(itb, table_offset, &compressed, &src_len);
+ if (compressed) {
+ dest_len = SQFS_METADATA_BLOCK_SIZE;
+ ret = sqfs_decompress(&ctxt, *inode_table +
+ dest_offset, &dest_len,
+ src_table, src_len);
+ if (ret) {
+ free(*inode_table);
+ *inode_table = NULL;
+ goto free_itb;
+ }
+
+ dest_offset += dest_len;
+ } else {
+ memcpy(*inode_table + (j * SQFS_METADATA_BLOCK_SIZE),
+ src_table, src_len);
+ }
+
+ /*
+ * Offsets to the decompression destination, to the metadata
+ * buffer 'itb' and to the decompression source, respectively.
+ */
+
+ table_offset += src_len + SQFS_HEADER_SIZE;
+ src_table += src_len + SQFS_HEADER_SIZE;
+ }
+
+free_itb:
+ free(itb);
+
+ return ret;
+}
+
+static int sqfs_read_directory_table(unsigned char **dir_table, u32 **pos_list)
+{
+ u64 start, n_blks, table_offset, table_size;
+ struct squashfs_super_block *sblk = ctxt.sblk;
+ int j, ret = 0, metablks_count = -1;
+ unsigned char *src_table, *dtb;
+ u32 src_len, dest_offset = 0;
+ unsigned long dest_len = 0;
+ bool compressed;
+
+ *dir_table = NULL;
+ *pos_list = NULL;
+ /* DIRECTORY TABLE */
+ table_size = get_unaligned_le64(&sblk->fragment_table_start) -
+ get_unaligned_le64(&sblk->directory_table_start);
+ start = get_unaligned_le64(&sblk->directory_table_start) /
+ ctxt.cur_dev->blksz;
+ n_blks = sqfs_calc_n_blks(sblk->directory_table_start,
+ sblk->fragment_table_start, &table_offset);
+
+ /* Allocate a proper sized buffer (dtb) to store the directory table */
+ dtb = malloc_cache_aligned(n_blks * ctxt.cur_dev->blksz);
+ if (!dtb)
+ return -ENOMEM;
+
+ if (sqfs_disk_read(start, n_blks, dtb) < 0)
+ goto out;
+
+ /* Parse directory table (metadata block) header */
+ ret = sqfs_read_metablock(dtb, table_offset, &compressed, &src_len);
+ if (ret)
+ goto out;
+
+ /* Calculate total size to store the whole decompressed table */
+ metablks_count = sqfs_count_metablks(dtb, table_offset, table_size);
+ if (metablks_count < 1)
+ goto out;
+
+ *dir_table = malloc(metablks_count * SQFS_METADATA_BLOCK_SIZE);
+ if (!*dir_table)
+ goto out;
+
+ *pos_list = malloc(metablks_count * sizeof(u32));
+ if (!*pos_list)
+ goto out;
+
+ ret = sqfs_get_metablk_pos(*pos_list, dtb, table_offset,
+ metablks_count);
+ if (ret) {
+ metablks_count = -1;
+ goto out;
+ }
+
+ src_table = dtb + table_offset + SQFS_HEADER_SIZE;
+
+ /* Extract compressed Directory table */
+ dest_offset = 0;
+ for (j = 0; j < metablks_count; j++) {
+ sqfs_read_metablock(dtb, table_offset, &compressed, &src_len);
+ if (compressed) {
+ dest_len = SQFS_METADATA_BLOCK_SIZE;
+ ret = sqfs_decompress(&ctxt, *dir_table +
+ (j * SQFS_METADATA_BLOCK_SIZE),
+ &dest_len, src_table, src_len);
+ if (ret) {
+ metablks_count = -1;
+ goto out;
+ }
+
+ if (dest_len < SQFS_METADATA_BLOCK_SIZE) {
+ dest_offset += dest_len;
+ break;
+ }
+
+ dest_offset += dest_len;
+ } else {
+ memcpy(*dir_table + (j * SQFS_METADATA_BLOCK_SIZE),
+ src_table, src_len);
+ }
+
+ /*
+ * Offsets to the decompression destination, to the metadata
+ * buffer 'dtb' and to the decompression source, respectively.
+ */
+ table_offset += src_len + SQFS_HEADER_SIZE;
+ src_table += src_len + SQFS_HEADER_SIZE;
+ }
+
+out:
+ if (metablks_count < 1) {
+ free(*dir_table);
+ free(*pos_list);
+ *dir_table = NULL;
+ *pos_list = NULL;
+ }
+ free(dtb);
+
+ return metablks_count;
+}
+
+int sqfs_opendir(const char *filename, struct fs_dir_stream **dirsp)
+{
+ unsigned char *inode_table = NULL, *dir_table = NULL;
+ int j, token_count = 0, ret = 0, metablks_count;
+ struct squashfs_dir_stream *dirs;
+ char **token_list = NULL, *path = NULL;
+ u32 *pos_list = NULL;
+
+ dirs = calloc(1, sizeof(*dirs));
+ if (!dirs)
+ return -EINVAL;
+
+ /* these should be set to NULL to prevent dangling pointers */
+ dirs->dir_header = NULL;
+ dirs->entry = NULL;
+ dirs->table = NULL;
+ dirs->inode_table = NULL;
+ dirs->dir_table = NULL;
+
+ ret = sqfs_read_inode_table(&inode_table);
+ if (ret) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ metablks_count = sqfs_read_directory_table(&dir_table, &pos_list);
+ if (metablks_count < 1) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Tokenize filename */
+ token_count = sqfs_count_tokens(filename);
+ if (token_count < 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ path = strdup(filename);
+ if (!path) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ token_list = malloc(token_count * sizeof(char *));
+ if (!token_list) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Fill tokens list */
+ ret = sqfs_tokenize(token_list, token_count, path);
+ if (ret)
+ goto out;
+ /*
+ * ldir's (extended directory) size is greater than dir, so it works as
+ * a general solution for the malloc size, since 'i' is a union.
+ */
+ dirs->inode_table = inode_table;
+ dirs->dir_table = dir_table;
+ ret = sqfs_search_dir(dirs, token_list, token_count, pos_list,
+ metablks_count);
+ if (ret)
+ goto out;
+
+ if (le16_to_cpu(dirs->i_dir.inode_type) == SQFS_DIR_TYPE)
+ dirs->size = le16_to_cpu(dirs->i_dir.file_size);
+ else
+ dirs->size = le32_to_cpu(dirs->i_ldir.file_size);
+
+ /* Setup directory header */
+ memcpy(dirs->dir_header, dirs->table, SQFS_DIR_HEADER_SIZE);
+ dirs->entry_count = dirs->dir_header->count + 1;
+ dirs->size -= SQFS_DIR_HEADER_SIZE;
+
+ /* Setup entry */
+ dirs->entry = NULL;
+ dirs->table += SQFS_DIR_HEADER_SIZE;
+
+ *dirsp = (struct fs_dir_stream *)dirs;
+
+out:
+ for (j = 0; j < token_count; j++)
+ free(token_list[j]);
+ free(token_list);
+ free(pos_list);
+ free(path);
+ if (ret) {
+ free(inode_table);
+ free(dirs);
+ }
+
+ return ret;
+}
+
+int sqfs_readdir(struct fs_dir_stream *fs_dirs, struct fs_dirent **dentp)
+{
+ struct squashfs_super_block *sblk = ctxt.sblk;
+ struct squashfs_dir_stream *dirs;
+ struct squashfs_lreg_inode *lreg;
+ struct squashfs_base_inode *base;
+ struct squashfs_reg_inode *reg;
+ int i_number, offset = 0, ret;
+ struct fs_dirent *dent;
+ unsigned char *ipos;
+
+ dirs = (struct squashfs_dir_stream *)fs_dirs;
+ if (!dirs->size) {
+ *dentp = NULL;
+ return -SQFS_STOP_READDIR;
+ }
+
+ dent = &dirs->dentp;
+
+ if (!dirs->entry_count) {
+ if (dirs->size > SQFS_DIR_HEADER_SIZE) {
+ dirs->size -= SQFS_DIR_HEADER_SIZE;
+ } else {
+ *dentp = NULL;
+ dirs->size = 0;
+ return -SQFS_STOP_READDIR;
+ }
+
+ if (dirs->size > SQFS_EMPTY_FILE_SIZE) {
+ /* Read follow-up (emitted) dir. header */
+ memcpy(dirs->dir_header, dirs->table,
+ SQFS_DIR_HEADER_SIZE);
+ dirs->entry_count = dirs->dir_header->count + 1;
+ ret = sqfs_read_entry(&dirs->entry, dirs->table +
+ SQFS_DIR_HEADER_SIZE);
+ if (ret)
+ return -SQFS_STOP_READDIR;
+
+ dirs->table += SQFS_DIR_HEADER_SIZE;
+ }
+ } else {
+ ret = sqfs_read_entry(&dirs->entry, dirs->table);
+ if (ret)
+ return -SQFS_STOP_READDIR;
+ }
+
+ i_number = dirs->dir_header->inode_number + dirs->entry->inode_offset;
+ ipos = sqfs_find_inode(dirs->inode_table, i_number, sblk->inodes,
+ sblk->block_size);
+
+ base = (struct squashfs_base_inode *)ipos;
+
+ /* Set entry type and size */
+ switch (dirs->entry->type) {
+ case SQFS_DIR_TYPE:
+ case SQFS_LDIR_TYPE:
+ dent->type = FS_DT_DIR;
+ break;
+ case SQFS_REG_TYPE:
+ case SQFS_LREG_TYPE:
+ /*
+ * Entries do not differentiate extended from regular types, so
+ * it needs to be verified manually.
+ */
+ if (get_unaligned_le16(&base->inode_type) == SQFS_LREG_TYPE) {
+ lreg = (struct squashfs_lreg_inode *)ipos;
+ dent->size = get_unaligned_le64(&lreg->file_size);
+ } else {
+ reg = (struct squashfs_reg_inode *)ipos;
+ dent->size = get_unaligned_le32(&reg->file_size);
+ }
+
+ dent->type = FS_DT_REG;
+ break;
+ case SQFS_BLKDEV_TYPE:
+ case SQFS_CHRDEV_TYPE:
+ case SQFS_LBLKDEV_TYPE:
+ case SQFS_LCHRDEV_TYPE:
+ case SQFS_FIFO_TYPE:
+ case SQFS_SOCKET_TYPE:
+ case SQFS_LFIFO_TYPE:
+ case SQFS_LSOCKET_TYPE:
+ dent->type = SQFS_MISC_ENTRY_TYPE;
+ break;
+ case SQFS_SYMLINK_TYPE:
+ case SQFS_LSYMLINK_TYPE:
+ dent->type = FS_DT_LNK;
+ break;
+ default:
+ return -SQFS_STOP_READDIR;
+ }
+
+ /* Set entry name */
+ strncpy(dent->name, dirs->entry->name, dirs->entry->name_size + 1);
+ dent->name[dirs->entry->name_size + 1] = '\0';
+
+ offset = dirs->entry->name_size + 1 + SQFS_ENTRY_BASE_LENGTH;
+ dirs->entry_count--;
+
+ /* Decrement size to be read */
+ if (dirs->size > offset)
+ dirs->size -= offset;
+ else
+ dirs->size = 0;
+
+ /* Keep a reference to the current entry before incrementing it */
+ dirs->table += offset;
+
+ *dentp = dent;
+
+ return 0;
+}
+
+int sqfs_probe(struct blk_desc *fs_dev_desc, struct disk_partition *fs_partition)
+{
+ struct squashfs_super_block *sblk;
+ int ret;
+
+ ctxt.cur_dev = fs_dev_desc;
+ ctxt.cur_part_info = *fs_partition;
+
+ ret = sqfs_read_sblk(&sblk);
+ if (ret)
+ goto error;
+
+ /* Make sure it has a valid SquashFS magic number*/
+ if (get_unaligned_le32(&sblk->s_magic) != SQFS_MAGIC_NUMBER) {
+ printf("Bad magic number for SquashFS image.\n");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ ctxt.sblk = sblk;
+
+ ret = sqfs_decompressor_init(&ctxt);
+ if (ret) {
+ goto error;
+ }
+
+ return 0;
+error:
+ ctxt.cur_dev = NULL;
+ free(ctxt.sblk);
+ ctxt.sblk = NULL;
+ return ret;
+}
+
+static char *sqfs_basename(char *path)
+{
+ char *fname;
+
+ fname = path + strlen(path) - 1;
+ while (fname >= path) {
+ if (*fname == '/') {
+ fname++;
+ break;
+ }
+
+ fname--;
+ }
+
+ return fname;
+}
+
+static char *sqfs_dirname(char *path)
+{
+ char *fname;
+
+ fname = sqfs_basename(path);
+ --fname;
+ *fname = '\0';
+
+ return path;
+}
+
+/*
+ * Takes a path to file and splits it in two parts: the filename itself and the
+ * directory's path, e.g.:
+ * path: /path/to/file.txt
+ * file: file.txt
+ * dir: /path/to
+ */
+static int sqfs_split_path(char **file, char **dir, const char *path)
+{
+ char *dirc, *basec, *bname, *dname, *tmp_path;
+ int ret = 0;
+
+ *file = NULL;
+ *dir = NULL;
+ dirc = NULL;
+ basec = NULL;
+ bname = NULL;
+ dname = NULL;
+ tmp_path = NULL;
+
+ /* check for first slash in path*/
+ if (path[0] == '/') {
+ tmp_path = strdup(path);
+ if (!tmp_path) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ } else {
+ tmp_path = malloc(strlen(path) + 2);
+ if (!tmp_path) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ tmp_path[0] = '/';
+ strcpy(tmp_path + 1, path);
+ }
+
+ /* String duplicates */
+ dirc = strdup(tmp_path);
+ if (!dirc) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ basec = strdup(tmp_path);
+ if (!basec) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ dname = sqfs_dirname(dirc);
+ bname = sqfs_basename(basec);
+
+ *file = strdup(bname);
+
+ if (!*file) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (*dname == '\0') {
+ *dir = malloc(2);
+ if (!*dir) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ (*dir)[0] = '/';
+ (*dir)[1] = '\0';
+ } else {
+ *dir = strdup(dname);
+ if (!*dir) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ }
+
+out:
+ if (ret) {
+ free(*file);
+ free(*dir);
+ *dir = NULL;
+ *file = NULL;
+ }
+ free(basec);
+ free(dirc);
+ free(tmp_path);
+
+ return ret;
+}
+
+static int sqfs_get_regfile_info(struct squashfs_reg_inode *reg,
+ struct squashfs_file_info *finfo,
+ struct squashfs_fragment_block_entry *fentry,
+ __le32 blksz)
+{
+ int datablk_count = 0, ret;
+
+ finfo->size = get_unaligned_le32(&reg->file_size);
+ finfo->offset = get_unaligned_le32(&reg->offset);
+ finfo->start = get_unaligned_le32(&reg->start_block);
+ finfo->frag = SQFS_IS_FRAGMENTED(get_unaligned_le32(&reg->fragment));
+
+ if (finfo->frag && finfo->offset == 0xFFFFFFFF)
+ return -EINVAL;
+
+ if (finfo->size < 1 || finfo->start == 0xFFFFFFFF)
+ return -EINVAL;
+
+ if (finfo->frag) {
+ datablk_count = finfo->size / le32_to_cpu(blksz);
+ ret = sqfs_frag_lookup(get_unaligned_le32(&reg->fragment),
+ fentry);
+ if (ret < 0)
+ return -EINVAL;
+ finfo->comp = ret;
+ if (fentry->size < 1 || fentry->start == 0x7FFFFFFF)
+ return -EINVAL;
+ } else {
+ datablk_count = DIV_ROUND_UP(finfo->size, le32_to_cpu(blksz));
+ }
+
+ finfo->blk_sizes = malloc(datablk_count * sizeof(u32));
+ if (!finfo->blk_sizes)
+ return -ENOMEM;
+
+ return datablk_count;
+}
+
+static int sqfs_get_lregfile_info(struct squashfs_lreg_inode *lreg,
+ struct squashfs_file_info *finfo,
+ struct squashfs_fragment_block_entry *fentry,
+ __le32 blksz)
+{
+ int datablk_count = 0, ret;
+
+ finfo->size = get_unaligned_le64(&lreg->file_size);
+ finfo->offset = get_unaligned_le32(&lreg->offset);
+ finfo->start = get_unaligned_le64(&lreg->start_block);
+ finfo->frag = SQFS_IS_FRAGMENTED(get_unaligned_le32(&lreg->fragment));
+
+ if (finfo->frag && finfo->offset == 0xFFFFFFFF)
+ return -EINVAL;
+
+ if (finfo->size < 1 || finfo->start == 0x7FFFFFFF)
+ return -EINVAL;
+
+ if (finfo->frag) {
+ datablk_count = finfo->size / le32_to_cpu(blksz);
+ ret = sqfs_frag_lookup(get_unaligned_le32(&lreg->fragment),
+ fentry);
+ if (ret < 0)
+ return -EINVAL;
+ finfo->comp = ret;
+ if (fentry->size < 1 || fentry->start == 0x7FFFFFFF)
+ return -EINVAL;
+ } else {
+ datablk_count = DIV_ROUND_UP(finfo->size, le32_to_cpu(blksz));
+ }
+
+ finfo->blk_sizes = malloc(datablk_count * sizeof(u32));
+ if (!finfo->blk_sizes)
+ return -ENOMEM;
+
+ return datablk_count;
+}
+
+int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
+ loff_t *actread)
+{
+ char *dir = NULL, *fragment_block, *datablock = NULL, *data_buffer = NULL;
+ char *fragment = NULL, *file = NULL, *resolved, *data;
+ u64 start, n_blks, table_size, data_offset, table_offset, sparse_size;
+ int ret, j, i_number, datablk_count = 0;
+ struct squashfs_super_block *sblk = ctxt.sblk;
+ struct squashfs_fragment_block_entry frag_entry;
+ struct squashfs_file_info finfo = {0};
+ struct squashfs_symlink_inode *symlink;
+ struct fs_dir_stream *dirsp = NULL;
+ struct squashfs_dir_stream *dirs;
+ struct squashfs_lreg_inode *lreg;
+ struct squashfs_base_inode *base;
+ struct squashfs_reg_inode *reg;
+ unsigned long dest_len;
+ struct fs_dirent *dent;
+ unsigned char *ipos;
+
+ *actread = 0;
+
+ if (offset) {
+ /*
+ * TODO: implement reading at an offset in file
+ */
+ printf("Error: reading at a specific offset in a squashfs file is not supported yet.\n");
+ return -EINVAL;
+ }
+
+ /*
+ * sqfs_opendir will uncompress inode and directory tables, and will
+ * return a pointer to the directory that contains the requested file.
+ */
+ sqfs_split_path(&file, &dir, filename);
+ ret = sqfs_opendir(dir, &dirsp);
+ if (ret) {
+ goto out;
+ }
+
+ dirs = (struct squashfs_dir_stream *)dirsp;
+
+ /* For now, only regular files are able to be loaded */
+ while (!sqfs_readdir(dirsp, &dent)) {
+ ret = strcmp(dent->name, file);
+ if (!ret)
+ break;
+
+ free(dirs->entry);
+ dirs->entry = NULL;
+ }
+
+ if (ret) {
+ printf("File not found.\n");
+ *actread = 0;
+ ret = -ENOENT;
+ goto out;
+ }
+
+ i_number = dirs->dir_header->inode_number + dirs->entry->inode_offset;
+ ipos = sqfs_find_inode(dirs->inode_table, i_number, sblk->inodes,
+ sblk->block_size);
+
+ base = (struct squashfs_base_inode *)ipos;
+ switch (get_unaligned_le16(&base->inode_type)) {
+ case SQFS_REG_TYPE:
+ reg = (struct squashfs_reg_inode *)ipos;
+ datablk_count = sqfs_get_regfile_info(reg, &finfo, &frag_entry,
+ sblk->block_size);
+ if (datablk_count < 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ memcpy(finfo.blk_sizes, ipos + sizeof(*reg),
+ datablk_count * sizeof(u32));
+ break;
+ case SQFS_LREG_TYPE:
+ lreg = (struct squashfs_lreg_inode *)ipos;
+ datablk_count = sqfs_get_lregfile_info(lreg, &finfo,
+ &frag_entry,
+ sblk->block_size);
+ if (datablk_count < 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ memcpy(finfo.blk_sizes, ipos + sizeof(*lreg),
+ datablk_count * sizeof(u32));
+ break;
+ case SQFS_SYMLINK_TYPE:
+ case SQFS_LSYMLINK_TYPE:
+ symlink = (struct squashfs_symlink_inode *)ipos;
+ resolved = sqfs_resolve_symlink(symlink, filename);
+ ret = sqfs_read(resolved, buf, offset, len, actread);
+ free(resolved);
+ goto out;
+ case SQFS_BLKDEV_TYPE:
+ case SQFS_CHRDEV_TYPE:
+ case SQFS_LBLKDEV_TYPE:
+ case SQFS_LCHRDEV_TYPE:
+ case SQFS_FIFO_TYPE:
+ case SQFS_SOCKET_TYPE:
+ case SQFS_LFIFO_TYPE:
+ case SQFS_LSOCKET_TYPE:
+ default:
+ printf("Unsupported entry type\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* If the user specifies a length, check its sanity */
+ if (len) {
+ if (len > finfo.size) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ finfo.size = len;
+ } else {
+ len = finfo.size;
+ }
+
+ if (datablk_count) {
+ data_offset = finfo.start;
+ datablock = malloc(get_unaligned_le32(&sblk->block_size));
+ if (!datablock) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ }
+
+ for (j = 0; j < datablk_count; j++) {
+ start = data_offset / ctxt.cur_dev->blksz;
+ table_size = SQFS_BLOCK_SIZE(finfo.blk_sizes[j]);
+ table_offset = data_offset - (start * ctxt.cur_dev->blksz);
+ n_blks = DIV_ROUND_UP(table_size + table_offset,
+ ctxt.cur_dev->blksz);
+
+ /* Don't load any data for sparse blocks */
+ if (finfo.blk_sizes[j] == 0) {
+ n_blks = 0;
+ table_offset = 0;
+ data_buffer = NULL;
+ data = NULL;
+ } else {
+ data_buffer = malloc_cache_aligned(n_blks * ctxt.cur_dev->blksz);
+
+ if (!data_buffer) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = sqfs_disk_read(start, n_blks, data_buffer);
+ if (ret < 0) {
+ /*
+ * Possible causes: too many data blocks or too large
+ * SquashFS block size. Tip: re-compile the SquashFS
+ * image with mksquashfs's -b <block_size> option.
+ */
+ printf("Error: too many data blocks to be read.\n");
+ goto out;
+ }
+
+ data = data_buffer + table_offset;
+ }
+
+ /* Load the data */
+ if (finfo.blk_sizes[j] == 0) {
+ /* This is a sparse block */
+ sparse_size = get_unaligned_le32(&sblk->block_size);
+ if ((*actread + sparse_size) > len)
+ sparse_size = len - *actread;
+ memset(buf + *actread, 0, sparse_size);
+ *actread += sparse_size;
+ } else if (SQFS_COMPRESSED_BLOCK(finfo.blk_sizes[j])) {
+ dest_len = get_unaligned_le32(&sblk->block_size);
+ ret = sqfs_decompress(&ctxt, datablock, &dest_len,
+ data, table_size);
+ if (ret)
+ goto out;
+
+ if ((*actread + dest_len) > len)
+ dest_len = len - *actread;
+ memcpy(buf + *actread, datablock, dest_len);
+ *actread += dest_len;
+ } else {
+ if ((*actread + table_size) > len)
+ table_size = len - *actread;
+ memcpy(buf + *actread, data, table_size);
+ *actread += table_size;
+ }
+
+ data_offset += table_size;
+ if (data_buffer)
+ free(data_buffer);
+ data_buffer = NULL;
+ if (*actread >= len)
+ break;
+ }
+
+ /*
+ * There is no need to continue if the file is not fragmented.
+ */
+ if (!finfo.frag) {
+ ret = 0;
+ goto out;
+ }
+
+ start = frag_entry.start / ctxt.cur_dev->blksz;
+ table_size = SQFS_BLOCK_SIZE(frag_entry.size);
+ table_offset = frag_entry.start - (start * ctxt.cur_dev->blksz);
+ n_blks = DIV_ROUND_UP(table_size + table_offset, ctxt.cur_dev->blksz);
+
+ fragment = malloc_cache_aligned(n_blks * ctxt.cur_dev->blksz);
+
+ if (!fragment) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = sqfs_disk_read(start, n_blks, fragment);
+ if (ret < 0)
+ goto out;
+
+ /* File compressed and fragmented */
+ if (finfo.frag && finfo.comp) {
+ dest_len = get_unaligned_le32(&sblk->block_size);
+ fragment_block = malloc(dest_len);
+ if (!fragment_block) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = sqfs_decompress(&ctxt, fragment_block, &dest_len,
+ (void *)fragment + table_offset,
+ frag_entry.size);
+ if (ret) {
+ free(fragment_block);
+ goto out;
+ }
+
+ memcpy(buf + *actread, &fragment_block[finfo.offset], finfo.size - *actread);
+ *actread = finfo.size;
+
+ free(fragment_block);
+
+ } else if (finfo.frag && !finfo.comp) {
+ fragment_block = (void *)fragment + table_offset;
+
+ memcpy(buf + *actread, &fragment_block[finfo.offset], finfo.size - *actread);
+ *actread = finfo.size;
+ }
+
+out:
+ free(fragment);
+ if (datablk_count) {
+ free(data_buffer);
+ free(datablock);
+ }
+ free(file);
+ free(dir);
+ free(finfo.blk_sizes);
+ sqfs_closedir(dirsp);
+
+ return ret;
+}
+
+int sqfs_size(const char *filename, loff_t *size)
+{
+ struct squashfs_super_block *sblk = ctxt.sblk;
+ struct squashfs_symlink_inode *symlink;
+ struct fs_dir_stream *dirsp = NULL;
+ struct squashfs_base_inode *base;
+ struct squashfs_dir_stream *dirs;
+ struct squashfs_lreg_inode *lreg;
+ struct squashfs_reg_inode *reg;
+ char *dir, *file, *resolved;
+ struct fs_dirent *dent;
+ unsigned char *ipos;
+ int ret, i_number;
+
+ sqfs_split_path(&file, &dir, filename);
+ /*
+ * sqfs_opendir will uncompress inode and directory tables, and will
+ * return a pointer to the directory that contains the requested file.
+ */
+ ret = sqfs_opendir(dir, &dirsp);
+ if (ret) {
+ ret = -EINVAL;
+ goto free_strings;
+ }
+
+ dirs = (struct squashfs_dir_stream *)dirsp;
+
+ while (!sqfs_readdir(dirsp, &dent)) {
+ ret = strcmp(dent->name, file);
+ if (!ret)
+ break;
+ free(dirs->entry);
+ dirs->entry = NULL;
+ }
+
+ if (ret) {
+ printf("File not found.\n");
+ *size = 0;
+ ret = -EINVAL;
+ goto free_strings;
+ }
+
+ i_number = dirs->dir_header->inode_number + dirs->entry->inode_offset;
+ ipos = sqfs_find_inode(dirs->inode_table, i_number, sblk->inodes,
+ sblk->block_size);
+ free(dirs->entry);
+ dirs->entry = NULL;
+
+ base = (struct squashfs_base_inode *)ipos;
+ switch (get_unaligned_le16(&base->inode_type)) {
+ case SQFS_REG_TYPE:
+ reg = (struct squashfs_reg_inode *)ipos;
+ *size = get_unaligned_le32(&reg->file_size);
+ break;
+ case SQFS_LREG_TYPE:
+ lreg = (struct squashfs_lreg_inode *)ipos;
+ *size = get_unaligned_le64(&lreg->file_size);
+ break;
+ case SQFS_SYMLINK_TYPE:
+ case SQFS_LSYMLINK_TYPE:
+ symlink = (struct squashfs_symlink_inode *)ipos;
+ resolved = sqfs_resolve_symlink(symlink, filename);
+ ret = sqfs_size(resolved, size);
+ free(resolved);
+ break;
+ case SQFS_BLKDEV_TYPE:
+ case SQFS_CHRDEV_TYPE:
+ case SQFS_LBLKDEV_TYPE:
+ case SQFS_LCHRDEV_TYPE:
+ case SQFS_FIFO_TYPE:
+ case SQFS_SOCKET_TYPE:
+ case SQFS_LFIFO_TYPE:
+ case SQFS_LSOCKET_TYPE:
+ default:
+ printf("Unable to recover entry's size.\n");
+ *size = 0;
+ ret = -EINVAL;
+ break;
+ }
+
+free_strings:
+ free(dir);
+ free(file);
+
+ sqfs_closedir(dirsp);
+
+ return ret;
+}
+
+int sqfs_exists(const char *filename)
+{
+ struct fs_dir_stream *dirsp = NULL;
+ struct squashfs_dir_stream *dirs;
+ char *dir, *file;
+ struct fs_dirent *dent;
+ int ret;
+
+ sqfs_split_path(&file, &dir, filename);
+ /*
+ * sqfs_opendir will uncompress inode and directory tables, and will
+ * return a pointer to the directory that contains the requested file.
+ */
+ ret = sqfs_opendir(dir, &dirsp);
+ if (ret) {
+ ret = -EINVAL;
+ goto free_strings;
+ }
+
+ dirs = (struct squashfs_dir_stream *)dirsp;
+
+ while (!sqfs_readdir(dirsp, &dent)) {
+ ret = strcmp(dent->name, file);
+ if (!ret)
+ break;
+ free(dirs->entry);
+ dirs->entry = NULL;
+ }
+
+ sqfs_closedir(dirsp);
+
+free_strings:
+ free(dir);
+ free(file);
+
+ return ret == 0;
+}
+
+void sqfs_close(void)
+{
+ sqfs_decompressor_cleanup(&ctxt);
+ free(ctxt.sblk);
+ ctxt.sblk = NULL;
+ ctxt.cur_dev = NULL;
+}
+
+void sqfs_closedir(struct fs_dir_stream *dirs)
+{
+ struct squashfs_dir_stream *sqfs_dirs;
+
+ if (!dirs)
+ return;
+
+ sqfs_dirs = (struct squashfs_dir_stream *)dirs;
+ free(sqfs_dirs->inode_table);
+ free(sqfs_dirs->dir_table);
+ free(sqfs_dirs->dir_header);
+ free(sqfs_dirs);
+}
diff --git a/roms/u-boot/fs/squashfs/sqfs_decompressor.c b/roms/u-boot/fs/squashfs/sqfs_decompressor.c
new file mode 100644
index 000000000..d69ddb24a
--- /dev/null
+++ b/roms/u-boot/fs/squashfs/sqfs_decompressor.c
@@ -0,0 +1,155 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Bootlin
+ *
+ * Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
+ */
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#if IS_ENABLED(CONFIG_LZO)
+#include <linux/lzo.h>
+#endif
+
+#if IS_ENABLED(CONFIG_ZLIB)
+#include <u-boot/zlib.h>
+#endif
+
+#if IS_ENABLED(CONFIG_ZSTD)
+#include <linux/zstd.h>
+#endif
+
+#include "sqfs_decompressor.h"
+#include "sqfs_utils.h"
+
+int sqfs_decompressor_init(struct squashfs_ctxt *ctxt)
+{
+ u16 comp_type = get_unaligned_le16(&ctxt->sblk->compression);
+
+ switch (comp_type) {
+#if IS_ENABLED(CONFIG_LZO)
+ case SQFS_COMP_LZO:
+ break;
+#endif
+#if IS_ENABLED(CONFIG_ZLIB)
+ case SQFS_COMP_ZLIB:
+ break;
+#endif
+#if IS_ENABLED(CONFIG_ZSTD)
+ case SQFS_COMP_ZSTD:
+ ctxt->zstd_workspace = malloc(ZSTD_DCtxWorkspaceBound());
+ if (!ctxt->zstd_workspace)
+ return -ENOMEM;
+ break;
+#endif
+ default:
+ printf("Error: unknown compression type.\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+void sqfs_decompressor_cleanup(struct squashfs_ctxt *ctxt)
+{
+ u16 comp_type = get_unaligned_le16(&ctxt->sblk->compression);
+
+ switch (comp_type) {
+#if IS_ENABLED(CONFIG_LZO)
+ case SQFS_COMP_LZO:
+ break;
+#endif
+#if IS_ENABLED(CONFIG_ZLIB)
+ case SQFS_COMP_ZLIB:
+ break;
+#endif
+#if IS_ENABLED(CONFIG_ZSTD)
+ case SQFS_COMP_ZSTD:
+ free(ctxt->zstd_workspace);
+ break;
+#endif
+ }
+}
+
+#if IS_ENABLED(CONFIG_ZLIB)
+static void zlib_decompression_status(int ret)
+{
+ switch (ret) {
+ case Z_BUF_ERROR:
+ printf("Error: 'dest' buffer is not large enough.\n");
+ break;
+ case Z_DATA_ERROR:
+ printf("Error: corrupted compressed data.\n");
+ break;
+ case Z_MEM_ERROR:
+ printf("Error: insufficient memory.\n");
+ break;
+ }
+}
+#endif
+
+#if IS_ENABLED(CONFIG_ZSTD)
+static int sqfs_zstd_decompress(struct squashfs_ctxt *ctxt, void *dest,
+ unsigned long dest_len, void *source, u32 src_len)
+{
+ ZSTD_DCtx *ctx;
+ size_t wsize;
+ int ret;
+
+ wsize = ZSTD_DCtxWorkspaceBound();
+ ctx = ZSTD_initDCtx(ctxt->zstd_workspace, wsize);
+ ret = ZSTD_decompressDCtx(ctx, dest, dest_len, source, src_len);
+
+ return ZSTD_isError(ret);
+}
+#endif /* CONFIG_ZSTD */
+
+int sqfs_decompress(struct squashfs_ctxt *ctxt, void *dest,
+ unsigned long *dest_len, void *source, u32 src_len)
+{
+ u16 comp_type = get_unaligned_le16(&ctxt->sblk->compression);
+ int ret = 0;
+
+ switch (comp_type) {
+#if IS_ENABLED(CONFIG_LZO)
+ case SQFS_COMP_LZO: {
+ size_t lzo_dest_len = *dest_len;
+ ret = lzo1x_decompress_safe(source, src_len, dest, &lzo_dest_len);
+ if (ret) {
+ printf("LZO decompression failed. Error code: %d\n", ret);
+ return -EINVAL;
+ }
+
+ break;
+ }
+#endif
+#if IS_ENABLED(CONFIG_ZLIB)
+ case SQFS_COMP_ZLIB:
+ ret = uncompress(dest, dest_len, source, src_len);
+ if (ret) {
+ zlib_decompression_status(ret);
+ return -EINVAL;
+ }
+
+ break;
+#endif
+#if IS_ENABLED(CONFIG_ZSTD)
+ case SQFS_COMP_ZSTD:
+ ret = sqfs_zstd_decompress(ctxt, dest, *dest_len, source, src_len);
+ if (ret) {
+ printf("ZSTD Error code: %d\n", ZSTD_getErrorCode(ret));
+ return -EINVAL;
+ }
+
+ break;
+#endif
+ default:
+ printf("Error: unknown compression type.\n");
+ return -EINVAL;
+ }
+
+ return ret;
+}
diff --git a/roms/u-boot/fs/squashfs/sqfs_decompressor.h b/roms/u-boot/fs/squashfs/sqfs_decompressor.h
new file mode 100644
index 000000000..892cfb697
--- /dev/null
+++ b/roms/u-boot/fs/squashfs/sqfs_decompressor.h
@@ -0,0 +1,61 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Bootlin
+ *
+ * Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
+ */
+
+#ifndef SQFS_DECOMPRESSOR_H
+#define SQFS_DECOMPRESSOR_H
+
+#include <stdint.h>
+#include "sqfs_filesystem.h"
+
+#define SQFS_COMP_ZLIB 1
+#define SQFS_COMP_LZMA 2
+#define SQFS_COMP_LZO 3
+#define SQFS_COMP_XZ 4
+#define SQFS_COMP_LZ4 5
+#define SQFS_COMP_ZSTD 6
+
+/* LZMA does not support any compression options */
+
+struct squashfs_gzip_opts {
+ u32 compression_level;
+ u16 window_size;
+ u16 strategies;
+};
+
+struct squashfs_xz_opts {
+ u32 dictionary_size;
+ u32 executable_filters;
+};
+
+struct squashfs_lz4_opts {
+ u32 version;
+ u32 flags;
+};
+
+struct squashfs_zstd_opts {
+ u32 compression_level;
+};
+
+struct squashfs_lzo_opts {
+ u32 algorithm;
+ u32 level;
+};
+
+union squashfs_compression_opts {
+ struct squashfs_gzip_opts *gzip;
+ struct squashfs_xz_opts *xz;
+ struct squashfs_lz4_opts *lz4;
+ struct squashfs_zstd_opts *zstd;
+ struct squashfs_lzo_opts *lzo;
+};
+
+int sqfs_decompress(struct squashfs_ctxt *ctxt, void *dest,
+ unsigned long *dest_len, void *source, u32 src_len);
+int sqfs_decompressor_init(struct squashfs_ctxt *ctxt);
+void sqfs_decompressor_cleanup(struct squashfs_ctxt *ctxt);
+
+#endif /* SQFS_DECOMPRESSOR_H */
diff --git a/roms/u-boot/fs/squashfs/sqfs_dir.c b/roms/u-boot/fs/squashfs/sqfs_dir.c
new file mode 100644
index 000000000..a265b98fe
--- /dev/null
+++ b/roms/u-boot/fs/squashfs/sqfs_dir.c
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Bootlin
+ *
+ * Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
+ */
+
+#include <errno.h>
+#include <linux/types.h>
+#include <linux/byteorder/little_endian.h>
+#include <linux/byteorder/generic.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "sqfs_filesystem.h"
+#include "sqfs_utils.h"
+
+bool sqfs_is_dir(u16 type)
+{
+ return type == SQFS_DIR_TYPE || type == SQFS_LDIR_TYPE;
+}
+
+/*
+ * Receives a pointer (void *) to a position in the inode table containing the
+ * directory's inode. Returns directory inode offset into the directory table.
+ * m_list contains each metadata block's position, and m_count is the number of
+ * elements of m_list. Those metadata blocks come from the compressed directory
+ * table.
+ */
+int sqfs_dir_offset(void *dir_i, u32 *m_list, int m_count)
+{
+ struct squashfs_base_inode *base = dir_i;
+ struct squashfs_ldir_inode *ldir;
+ struct squashfs_dir_inode *dir;
+ u32 start_block;
+ int j, offset;
+
+ switch (get_unaligned_le16(&base->inode_type)) {
+ case SQFS_DIR_TYPE:
+ dir = (struct squashfs_dir_inode *)base;
+ start_block = get_unaligned_le32(&dir->start_block);
+ offset = get_unaligned_le16(&dir->offset);
+ break;
+ case SQFS_LDIR_TYPE:
+ ldir = (struct squashfs_ldir_inode *)base;
+ start_block = get_unaligned_le32(&ldir->start_block);
+ offset = get_unaligned_le16(&ldir->offset);
+ break;
+ default:
+ printf("Error: this is not a directory.\n");
+ return -EINVAL;
+ }
+
+ if (offset < 0)
+ return -EINVAL;
+
+ for (j = 0; j < m_count; j++) {
+ if (m_list[j] == start_block)
+ return (++j * SQFS_METADATA_BLOCK_SIZE) + offset;
+ }
+
+ if (start_block == 0)
+ return offset;
+
+ printf("Error: invalid inode reference to directory table.\n");
+
+ return -EINVAL;
+}
+
+bool sqfs_is_empty_dir(void *dir_i)
+{
+ struct squashfs_base_inode *base = dir_i;
+ struct squashfs_ldir_inode *ldir;
+ struct squashfs_dir_inode *dir;
+ u32 file_size;
+
+ switch (get_unaligned_le16(&base->inode_type)) {
+ case SQFS_DIR_TYPE:
+ dir = (struct squashfs_dir_inode *)base;
+ file_size = get_unaligned_le16(&dir->file_size);
+ break;
+ case SQFS_LDIR_TYPE:
+ ldir = (struct squashfs_ldir_inode *)base;
+ file_size = get_unaligned_le16(&ldir->file_size);
+ break;
+ default:
+ printf("Error: this is not a directory.\n");
+ return false;
+ }
+
+ return file_size == SQFS_EMPTY_FILE_SIZE;
+}
diff --git a/roms/u-boot/fs/squashfs/sqfs_filesystem.h b/roms/u-boot/fs/squashfs/sqfs_filesystem.h
new file mode 100644
index 000000000..856cd15e3
--- /dev/null
+++ b/roms/u-boot/fs/squashfs/sqfs_filesystem.h
@@ -0,0 +1,310 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Bootlin
+ *
+ * Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
+ */
+
+#ifndef SQFS_FILESYSTEM_H
+#define SQFS_FILESYSTEM_H
+
+#include <asm/unaligned.h>
+#include <fs.h>
+#include <part.h>
+#include <stdint.h>
+
+#define SQFS_UNCOMPRESSED_DATA 0x0002
+#define SQFS_MAGIC_NUMBER 0x73717368
+/* The three first members of squashfs_dir_index make a total of 12 bytes */
+#define SQFS_DIR_INDEX_BASE_LENGTH 12
+/* size of metadata (inode and directory) blocks */
+#define SQFS_METADATA_BLOCK_SIZE 8192
+/* Max. number of fragment entries in a metadata block is 512 */
+#define SQFS_MAX_ENTRIES 512
+/* Metadata blocks start by a 2-byte length header */
+#define SQFS_HEADER_SIZE 2
+#define SQFS_LREG_INODE_MIN_SIZE 56
+#define SQFS_DIR_HEADER_SIZE 12
+#define SQFS_MISC_ENTRY_TYPE -1
+#define SQFS_EMPTY_FILE_SIZE 3
+#define SQFS_STOP_READDIR 1
+#define SQFS_EMPTY_DIR -1
+/*
+ * A directory entry object has a fixed length of 8 bytes, corresponding to its
+ * first four members, plus the size of the entry name, which is equal to
+ * 'entry_name' + 1 bytes.
+ */
+#define SQFS_ENTRY_BASE_LENGTH 8
+/* Inode types */
+#define SQFS_DIR_TYPE 1
+#define SQFS_REG_TYPE 2
+#define SQFS_SYMLINK_TYPE 3
+#define SQFS_BLKDEV_TYPE 4
+#define SQFS_CHRDEV_TYPE 5
+#define SQFS_FIFO_TYPE 6
+#define SQFS_SOCKET_TYPE 7
+#define SQFS_LDIR_TYPE 8
+#define SQFS_LREG_TYPE 9
+#define SQFS_LSYMLINK_TYPE 10
+#define SQFS_LBLKDEV_TYPE 11
+#define SQFS_LCHRDEV_TYPE 12
+#define SQFS_LFIFO_TYPE 13
+#define SQFS_LSOCKET_TYPE 14
+
+struct squashfs_super_block {
+ __le32 s_magic;
+ __le32 inodes;
+ __le32 mkfs_time;
+ __le32 block_size;
+ __le32 fragments;
+ __le16 compression;
+ __le16 block_log;
+ __le16 flags;
+ __le16 no_ids;
+ __le16 s_major;
+ __le16 s_minor;
+ __le64 root_inode;
+ __le64 bytes_used;
+ __le64 id_table_start;
+ __le64 xattr_id_table_start;
+ __le64 inode_table_start;
+ __le64 directory_table_start;
+ __le64 fragment_table_start;
+ __le64 export_table_start;
+};
+
+struct squashfs_ctxt {
+ struct disk_partition cur_part_info;
+ struct blk_desc *cur_dev;
+ struct squashfs_super_block *sblk;
+#if IS_ENABLED(CONFIG_ZSTD)
+ void *zstd_workspace;
+#endif
+};
+
+struct squashfs_directory_index {
+ u32 index;
+ u32 start;
+ u32 size;
+ char name[0];
+};
+
+struct squashfs_base_inode {
+ __le16 inode_type;
+ __le16 mode;
+ __le16 uid;
+ __le16 guid;
+ __le32 mtime;
+ __le32 inode_number;
+};
+
+struct squashfs_ipc_inode {
+ __le16 inode_type;
+ __le16 mode;
+ __le16 uid;
+ __le16 guid;
+ __le32 mtime;
+ __le32 inode_number;
+ __le32 nlink;
+};
+
+struct squashfs_lipc_inode {
+ __le16 inode_type;
+ __le16 mode;
+ __le16 uid;
+ __le16 guid;
+ __le32 mtime;
+ __le32 inode_number;
+ __le32 nlink;
+ __le32 xattr;
+};
+
+struct squashfs_dev_inode {
+ __le16 inode_type;
+ __le16 mode;
+ __le16 uid;
+ __le16 guid;
+ __le32 mtime;
+ __le32 inode_number;
+ __le32 nlink;
+ __le32 rdev;
+};
+
+struct squashfs_ldev_inode {
+ __le16 inode_type;
+ __le16 mode;
+ __le16 uid;
+ __le16 guid;
+ __le32 mtime;
+ __le32 inode_number;
+ __le32 nlink;
+ __le32 rdev;
+ __le32 xattr;
+};
+
+struct squashfs_symlink_inode {
+ __le16 inode_type;
+ __le16 mode;
+ __le16 uid;
+ __le16 guid;
+ __le32 mtime;
+ __le32 inode_number;
+ __le32 nlink;
+ __le32 symlink_size;
+ char symlink[0];
+};
+
+struct squashfs_reg_inode {
+ __le16 inode_type;
+ __le16 mode;
+ __le16 uid;
+ __le16 guid;
+ __le32 mtime;
+ __le32 inode_number;
+ __le32 start_block;
+ __le32 fragment;
+ __le32 offset;
+ __le32 file_size;
+ __le32 block_list[0];
+};
+
+struct squashfs_lreg_inode {
+ __le16 inode_type;
+ __le16 mode;
+ __le16 uid;
+ __le16 guid;
+ __le32 mtime;
+ __le32 inode_number;
+ __le64 start_block;
+ __le64 file_size;
+ __le64 sparse;
+ __le32 nlink;
+ __le32 fragment;
+ __le32 offset;
+ __le32 xattr;
+ __le32 block_list[0];
+};
+
+struct squashfs_dir_inode {
+ __le16 inode_type;
+ __le16 mode;
+ __le16 uid;
+ __le16 guid;
+ __le32 mtime;
+ __le32 inode_number;
+ __le32 start_block;
+ __le32 nlink;
+ __le16 file_size;
+ __le16 offset;
+ __le32 parent_inode;
+};
+
+struct squashfs_ldir_inode {
+ __le16 inode_type;
+ __le16 mode;
+ __le16 uid;
+ __le16 guid;
+ __le32 mtime;
+ __le32 inode_number;
+ __le32 nlink;
+ __le32 file_size;
+ __le32 start_block;
+ __le32 parent_inode;
+ __le16 i_count;
+ __le16 offset;
+ __le32 xattr;
+ struct squashfs_directory_index index[0];
+};
+
+union squashfs_inode {
+ struct squashfs_base_inode *base;
+ struct squashfs_dev_inode *dev;
+ struct squashfs_ldev_inode *ldev;
+ struct squashfs_symlink_inode *symlink;
+ struct squashfs_reg_inode *reg;
+ struct squashfs_lreg_inode *lreg;
+ struct squashfs_dir_inode *dir;
+ struct squashfs_ldir_inode *ldir;
+ struct squashfs_ipc_inode *ipc;
+ struct squashfs_lipc_inode *lipc;
+};
+
+struct squashfs_directory_entry {
+ u16 offset;
+ u16 inode_offset;
+ u16 type;
+ u16 name_size;
+ char name[0];
+};
+
+struct squashfs_directory_header {
+ u32 count;
+ u32 start;
+ u32 inode_number;
+};
+
+struct squashfs_fragment_block_entry {
+ u64 start;
+ u32 size;
+ u32 _unused;
+};
+
+struct squashfs_dir_stream {
+ struct fs_dir_stream fs_dirs;
+ struct fs_dirent dentp;
+ /*
+ * 'size' is the uncompressed size of the entire listing, including
+ * headers. 'entry_count' is the number of entries following a
+ * specific header. Both variables are decremented in sqfs_readdir() so
+ * the function knows when the end of the directory is reached.
+ */
+ size_t size;
+ int entry_count;
+ /* SquashFS structures */
+ struct squashfs_directory_header *dir_header;
+ struct squashfs_directory_entry *entry;
+ /*
+ * 'table' points to a position into the directory table. Both 'table'
+ * and 'inode' are defined for the first time in sqfs_opendir().
+ * 'table's value changes in sqfs_readdir().
+ */
+ unsigned char *table;
+ union squashfs_inode i;
+ struct squashfs_dir_inode i_dir;
+ struct squashfs_ldir_inode i_ldir;
+ /*
+ * References to the tables' beginnings. They are assigned in
+ * sqfs_opendir() and freed in sqfs_closedir().
+ */
+ unsigned char *inode_table;
+ unsigned char *dir_table;
+};
+
+struct squashfs_file_info {
+ /* File size in bytes (uncompressed) */
+ size_t size;
+ /* Reference to list of data blocks's sizes */
+ u32 *blk_sizes;
+ /* Offset into the fragment block */
+ u32 offset;
+ /* Offset in which the data blocks begin */
+ u64 start;
+ /* Is file fragmented? */
+ bool frag;
+ /* Compressed fragment */
+ bool comp;
+};
+
+void *sqfs_find_inode(void *inode_table, int inode_number, __le32 inode_count,
+ __le32 block_size);
+
+int sqfs_dir_offset(void *dir_i, u32 *m_list, int m_count);
+
+int sqfs_read_metablock(unsigned char *file_mapping, int offset,
+ bool *compressed, u32 *data_size);
+
+bool sqfs_is_empty_dir(void *dir_i);
+
+bool sqfs_is_dir(u16 type);
+
+#endif /* SQFS_FILESYSTEM_H */
diff --git a/roms/u-boot/fs/squashfs/sqfs_inode.c b/roms/u-boot/fs/squashfs/sqfs_inode.c
new file mode 100644
index 000000000..d25cfb53e
--- /dev/null
+++ b/roms/u-boot/fs/squashfs/sqfs_inode.c
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Bootlin
+ *
+ * Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
+ */
+
+#include <asm/unaligned.h>
+#include <compiler.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "sqfs_decompressor.h"
+#include "sqfs_filesystem.h"
+#include "sqfs_utils.h"
+
+int sqfs_inode_size(struct squashfs_base_inode *inode, u32 blk_size)
+{
+ switch (get_unaligned_le16(&inode->inode_type)) {
+ case SQFS_DIR_TYPE:
+ return sizeof(struct squashfs_dir_inode);
+
+ case SQFS_REG_TYPE: {
+ struct squashfs_reg_inode *reg =
+ (struct squashfs_reg_inode *)inode;
+ u32 fragment = get_unaligned_le32(&reg->fragment);
+ u32 file_size = get_unaligned_le32(&reg->file_size);
+ unsigned int blk_list_size;
+
+ if (SQFS_IS_FRAGMENTED(fragment))
+ blk_list_size = file_size / blk_size;
+ else
+ blk_list_size = DIV_ROUND_UP(file_size, blk_size);
+
+ return sizeof(*reg) + blk_list_size * sizeof(u32);
+ }
+
+ case SQFS_LDIR_TYPE: {
+ struct squashfs_ldir_inode *ldir =
+ (struct squashfs_ldir_inode *)inode;
+ u16 i_count = get_unaligned_le16(&ldir->i_count);
+ unsigned int index_list_size = 0, l = 0;
+ struct squashfs_directory_index *di;
+ u32 sz;
+
+ if (i_count == 0)
+ return sizeof(*ldir);
+
+ di = ldir->index;
+ while (l < i_count) {
+ sz = get_unaligned_le32(&di->size) + 1;
+ index_list_size += sz;
+ di = (void *)di + sizeof(*di) + sz;
+ l++;
+ }
+
+ return sizeof(*ldir) + index_list_size +
+ i_count * SQFS_DIR_INDEX_BASE_LENGTH;
+ }
+
+ case SQFS_LREG_TYPE: {
+ struct squashfs_lreg_inode *lreg =
+ (struct squashfs_lreg_inode *)inode;
+ u32 fragment = get_unaligned_le32(&lreg->fragment);
+ u64 file_size = get_unaligned_le64(&lreg->file_size);
+ unsigned int blk_list_size;
+
+ if (fragment == 0xFFFFFFFF)
+ blk_list_size = DIV_ROUND_UP(file_size, blk_size);
+ else
+ blk_list_size = file_size / blk_size;
+
+ return sizeof(*lreg) + blk_list_size * sizeof(u32);
+ }
+
+ case SQFS_SYMLINK_TYPE:
+ case SQFS_LSYMLINK_TYPE: {
+ struct squashfs_symlink_inode *symlink =
+ (struct squashfs_symlink_inode *)inode;
+
+ return sizeof(*symlink) +
+ get_unaligned_le32(&symlink->symlink_size);
+ }
+
+ case SQFS_BLKDEV_TYPE:
+ case SQFS_CHRDEV_TYPE:
+ return sizeof(struct squashfs_dev_inode);
+ case SQFS_LBLKDEV_TYPE:
+ case SQFS_LCHRDEV_TYPE:
+ return sizeof(struct squashfs_ldev_inode);
+ case SQFS_FIFO_TYPE:
+ case SQFS_SOCKET_TYPE:
+ return sizeof(struct squashfs_ipc_inode);
+ case SQFS_LFIFO_TYPE:
+ case SQFS_LSOCKET_TYPE:
+ return sizeof(struct squashfs_lipc_inode);
+ default:
+ printf("Error while searching inode: unknown type.\n");
+ return -EINVAL;
+ }
+}
+
+/*
+ * Given the uncompressed inode table, the inode to be found and the number of
+ * inodes in the table, return inode position in case of success.
+ */
+void *sqfs_find_inode(void *inode_table, int inode_number, __le32 inode_count,
+ __le32 block_size)
+{
+ struct squashfs_base_inode *base;
+ unsigned int offset = 0, k;
+ int sz;
+
+ if (!inode_table) {
+ printf("%s: Invalid pointer to inode table.\n", __func__);
+ return NULL;
+ }
+
+ for (k = 0; k < le32_to_cpu(inode_count); k++) {
+ base = inode_table + offset;
+ if (get_unaligned_le32(&base->inode_number) == inode_number)
+ return inode_table + offset;
+
+ sz = sqfs_inode_size(base, le32_to_cpu(block_size));
+ if (sz < 0)
+ return NULL;
+
+ offset += sz;
+ }
+
+ printf("Inode not found.\n");
+
+ return NULL;
+}
+
+int sqfs_read_metablock(unsigned char *file_mapping, int offset,
+ bool *compressed, u32 *data_size)
+{
+ const unsigned char *data;
+ u16 header;
+
+ if (!file_mapping)
+ return -EFAULT;
+ data = file_mapping + offset;
+
+ header = get_unaligned((u16 *)data);
+ if (!header)
+ return -EINVAL;
+
+ *compressed = SQFS_COMPRESSED_METADATA(header);
+ *data_size = SQFS_METADATA_SIZE(header);
+
+ if (*data_size > SQFS_METADATA_BLOCK_SIZE) {
+ printf("Invalid metatada block size: %d bytes.\n", *data_size);
+ return -EINVAL;
+ }
+
+ return 0;
+}
diff --git a/roms/u-boot/fs/squashfs/sqfs_utils.h b/roms/u-boot/fs/squashfs/sqfs_utils.h
new file mode 100644
index 000000000..1260abe22
--- /dev/null
+++ b/roms/u-boot/fs/squashfs/sqfs_utils.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Bootlin
+ *
+ * Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
+ */
+
+#ifndef SQFS_UTILS_H
+#define SQFS_UTILS_H
+
+#include <linux/bitops.h>
+#include <linux/kernel.h>
+#include <stdbool.h>
+
+#define SQFS_FRAGMENT_INDEX_OFFSET(A) ((A) % SQFS_MAX_ENTRIES)
+#define SQFS_FRAGMENT_INDEX(A) ((A) / SQFS_MAX_ENTRIES)
+#define SQFS_BLOCK_SIZE(A) ((A) & GENMASK(23, 0))
+#define SQFS_CHECK_FLAG(flag, bit) (((flag) >> (bit)) & 1)
+/* Useful for both fragment and data blocks */
+#define SQFS_COMPRESSED_BLOCK(A) (!((A) & BIT(24)))
+/* SQFS_COMPRESSED_DATA strictly used with super block's 'flags' member */
+#define SQFS_COMPRESSED_DATA(A) (!((A) & 0x0002))
+#define SQFS_IS_FRAGMENTED(A) ((A) != 0xFFFFFFFF)
+/*
+ * These two macros work as getters for a metada block header, retrieving the
+ * data size and if it is compressed/uncompressed
+ */
+#define SQFS_COMPRESSED_METADATA(A) (!((A) & BIT(15)))
+#define SQFS_METADATA_SIZE(A) ((A) & GENMASK(14, 0))
+
+struct squashfs_super_block_flags {
+ /* check: unused
+ * uncompressed_ids: not supported
+ */
+ bool uncompressed_inodes;
+ bool uncompressed_data;
+ bool check;
+ bool uncompressed_frags;
+ bool no_frags;
+ bool always_frags;
+ bool duplicates;
+ bool exportable;
+ bool uncompressed_xattrs;
+ bool no_xattrs;
+ bool compressor_options;
+ bool uncompressed_ids;
+};
+
+#endif /* SQFS_UTILS_H */
diff --git a/roms/u-boot/fs/ubifs/Kconfig b/roms/u-boot/fs/ubifs/Kconfig
new file mode 100644
index 000000000..9da35b8a3
--- /dev/null
+++ b/roms/u-boot/fs/ubifs/Kconfig
@@ -0,0 +1,6 @@
+config UBIFS_SILENCE_MSG
+ bool "UBIFS silence verbose messages"
+ default ENV_IS_IN_UBI
+ help
+ Make the verbose messages from UBIFS stop printing. This leaves
+ warnings and errors enabled.
diff --git a/roms/u-boot/fs/ubifs/Makefile b/roms/u-boot/fs/ubifs/Makefile
new file mode 100644
index 000000000..64d644729
--- /dev/null
+++ b/roms/u-boot/fs/ubifs/Makefile
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright 2006
+# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+#
+# (C) Copyright 2003
+# Pavel Bartusek, Sysgo Real-Time Solutions AG, pba@sysgo.de
+#
+
+obj-y := ubifs.o io.o super.o sb.o master.o lpt.o
+obj-y += lpt_commit.o scan.o lprops.o
+obj-y += tnc.o tnc_misc.o debug.o crc16.o budget.o
+obj-y += log.o orphan.o recovery.o replay.o gc.o
diff --git a/roms/u-boot/fs/ubifs/budget.c b/roms/u-boot/fs/ubifs/budget.c
new file mode 100644
index 000000000..514f15f58
--- /dev/null
+++ b/roms/u-boot/fs/ubifs/budget.c
@@ -0,0 +1,729 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ *
+ * Authors: Adrian Hunter
+ * Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * This file implements the budgeting sub-system which is responsible for UBIFS
+ * space management.
+ *
+ * Factors such as compression, wasted space at the ends of LEBs, space in other
+ * journal heads, the effect of updates on the index, and so on, make it
+ * impossible to accurately predict the amount of space needed. Consequently
+ * approximations are used.
+ */
+
+#include "ubifs.h"
+#ifndef __UBOOT__
+#include <log.h>
+#include <linux/writeback.h>
+#else
+#include <linux/err.h>
+#endif
+#include <linux/math64.h>
+
+/*
+ * When pessimistic budget calculations say that there is no enough space,
+ * UBIFS starts writing back dirty inodes and pages, doing garbage collection,
+ * or committing. The below constant defines maximum number of times UBIFS
+ * repeats the operations.
+ */
+#define MAX_MKSPC_RETRIES 3
+
+/*
+ * The below constant defines amount of dirty pages which should be written
+ * back at when trying to shrink the liability.
+ */
+#define NR_TO_WRITE 16
+
+#ifndef __UBOOT__
+/**
+ * shrink_liability - write-back some dirty pages/inodes.
+ * @c: UBIFS file-system description object
+ * @nr_to_write: how many dirty pages to write-back
+ *
+ * This function shrinks UBIFS liability by means of writing back some amount
+ * of dirty inodes and their pages.
+ *
+ * Note, this function synchronizes even VFS inodes which are locked
+ * (@i_mutex) by the caller of the budgeting function, because write-back does
+ * not touch @i_mutex.
+ */
+static void shrink_liability(struct ubifs_info *c, int nr_to_write)
+{
+ down_read(&c->vfs_sb->s_umount);
+ writeback_inodes_sb(c->vfs_sb, WB_REASON_FS_FREE_SPACE);
+ up_read(&c->vfs_sb->s_umount);
+}
+
+/**
+ * run_gc - run garbage collector.
+ * @c: UBIFS file-system description object
+ *
+ * This function runs garbage collector to make some more free space. Returns
+ * zero if a free LEB has been produced, %-EAGAIN if commit is required, and a
+ * negative error code in case of failure.
+ */
+static int run_gc(struct ubifs_info *c)
+{
+ int err, lnum;
+
+ /* Make some free space by garbage-collecting dirty space */
+ down_read(&c->commit_sem);
+ lnum = ubifs_garbage_collect(c, 1);
+ up_read(&c->commit_sem);
+ if (lnum < 0)
+ return lnum;
+
+ /* GC freed one LEB, return it to lprops */
+ dbg_budg("GC freed LEB %d", lnum);
+ err = ubifs_return_leb(c, lnum);
+ if (err)
+ return err;
+ return 0;
+}
+
+/**
+ * get_liability - calculate current liability.
+ * @c: UBIFS file-system description object
+ *
+ * This function calculates and returns current UBIFS liability, i.e. the
+ * amount of bytes UBIFS has "promised" to write to the media.
+ */
+static long long get_liability(struct ubifs_info *c)
+{
+ long long liab;
+
+ spin_lock(&c->space_lock);
+ liab = c->bi.idx_growth + c->bi.data_growth + c->bi.dd_growth;
+ spin_unlock(&c->space_lock);
+ return liab;
+}
+
+/**
+ * make_free_space - make more free space on the file-system.
+ * @c: UBIFS file-system description object
+ *
+ * This function is called when an operation cannot be budgeted because there
+ * is supposedly no free space. But in most cases there is some free space:
+ * o budgeting is pessimistic, so it always budgets more than it is actually
+ * needed, so shrinking the liability is one way to make free space - the
+ * cached data will take less space then it was budgeted for;
+ * o GC may turn some dark space into free space (budgeting treats dark space
+ * as not available);
+ * o commit may free some LEB, i.e., turn freeable LEBs into free LEBs.
+ *
+ * So this function tries to do the above. Returns %-EAGAIN if some free space
+ * was presumably made and the caller has to re-try budgeting the operation.
+ * Returns %-ENOSPC if it couldn't do more free space, and other negative error
+ * codes on failures.
+ */
+static int make_free_space(struct ubifs_info *c)
+{
+ int err, retries = 0;
+ long long liab1, liab2;
+
+ do {
+ liab1 = get_liability(c);
+ /*
+ * We probably have some dirty pages or inodes (liability), try
+ * to write them back.
+ */
+ dbg_budg("liability %lld, run write-back", liab1);
+ shrink_liability(c, NR_TO_WRITE);
+
+ liab2 = get_liability(c);
+ if (liab2 < liab1)
+ return -EAGAIN;
+
+ dbg_budg("new liability %lld (not shrunk)", liab2);
+
+ /* Liability did not shrink again, try GC */
+ dbg_budg("Run GC");
+ err = run_gc(c);
+ if (!err)
+ return -EAGAIN;
+
+ if (err != -EAGAIN && err != -ENOSPC)
+ /* Some real error happened */
+ return err;
+
+ dbg_budg("Run commit (retries %d)", retries);
+ err = ubifs_run_commit(c);
+ if (err)
+ return err;
+ } while (retries++ < MAX_MKSPC_RETRIES);
+
+ return -ENOSPC;
+}
+#endif
+
+/**
+ * ubifs_calc_min_idx_lebs - calculate amount of LEBs for the index.
+ * @c: UBIFS file-system description object
+ *
+ * This function calculates and returns the number of LEBs which should be kept
+ * for index usage.
+ */
+int ubifs_calc_min_idx_lebs(struct ubifs_info *c)
+{
+ int idx_lebs;
+ long long idx_size;
+
+ idx_size = c->bi.old_idx_sz + c->bi.idx_growth + c->bi.uncommitted_idx;
+ /* And make sure we have thrice the index size of space reserved */
+ idx_size += idx_size << 1;
+ /*
+ * We do not maintain 'old_idx_size' as 'old_idx_lebs'/'old_idx_bytes'
+ * pair, nor similarly the two variables for the new index size, so we
+ * have to do this costly 64-bit division on fast-path.
+ */
+ idx_lebs = div_u64(idx_size + c->idx_leb_size - 1, c->idx_leb_size);
+ /*
+ * The index head is not available for the in-the-gaps method, so add an
+ * extra LEB to compensate.
+ */
+ idx_lebs += 1;
+ if (idx_lebs < MIN_INDEX_LEBS)
+ idx_lebs = MIN_INDEX_LEBS;
+ return idx_lebs;
+}
+
+#ifndef __UBOOT__
+/**
+ * ubifs_calc_available - calculate available FS space.
+ * @c: UBIFS file-system description object
+ * @min_idx_lebs: minimum number of LEBs reserved for the index
+ *
+ * This function calculates and returns amount of FS space available for use.
+ */
+long long ubifs_calc_available(const struct ubifs_info *c, int min_idx_lebs)
+{
+ int subtract_lebs;
+ long long available;
+
+ available = c->main_bytes - c->lst.total_used;
+
+ /*
+ * Now 'available' contains theoretically available flash space
+ * assuming there is no index, so we have to subtract the space which
+ * is reserved for the index.
+ */
+ subtract_lebs = min_idx_lebs;
+
+ /* Take into account that GC reserves one LEB for its own needs */
+ subtract_lebs += 1;
+
+ /*
+ * The GC journal head LEB is not really accessible. And since
+ * different write types go to different heads, we may count only on
+ * one head's space.
+ */
+ subtract_lebs += c->jhead_cnt - 1;
+
+ /* We also reserve one LEB for deletions, which bypass budgeting */
+ subtract_lebs += 1;
+
+ available -= (long long)subtract_lebs * c->leb_size;
+
+ /* Subtract the dead space which is not available for use */
+ available -= c->lst.total_dead;
+
+ /*
+ * Subtract dark space, which might or might not be usable - it depends
+ * on the data which we have on the media and which will be written. If
+ * this is a lot of uncompressed or not-compressible data, the dark
+ * space cannot be used.
+ */
+ available -= c->lst.total_dark;
+
+ /*
+ * However, there is more dark space. The index may be bigger than
+ * @min_idx_lebs. Those extra LEBs are assumed to be available, but
+ * their dark space is not included in total_dark, so it is subtracted
+ * here.
+ */
+ if (c->lst.idx_lebs > min_idx_lebs) {
+ subtract_lebs = c->lst.idx_lebs - min_idx_lebs;
+ available -= subtract_lebs * c->dark_wm;
+ }
+
+ /* The calculations are rough and may end up with a negative number */
+ return available > 0 ? available : 0;
+}
+
+/**
+ * can_use_rp - check whether the user is allowed to use reserved pool.
+ * @c: UBIFS file-system description object
+ *
+ * UBIFS has so-called "reserved pool" which is flash space reserved
+ * for the superuser and for uses whose UID/GID is recorded in UBIFS superblock.
+ * This function checks whether current user is allowed to use reserved pool.
+ * Returns %1 current user is allowed to use reserved pool and %0 otherwise.
+ */
+static int can_use_rp(struct ubifs_info *c)
+{
+ if (uid_eq(current_fsuid(), c->rp_uid) || capable(CAP_SYS_RESOURCE) ||
+ (!gid_eq(c->rp_gid, GLOBAL_ROOT_GID) && in_group_p(c->rp_gid)))
+ return 1;
+ return 0;
+}
+
+/**
+ * do_budget_space - reserve flash space for index and data growth.
+ * @c: UBIFS file-system description object
+ *
+ * This function makes sure UBIFS has enough free LEBs for index growth and
+ * data.
+ *
+ * When budgeting index space, UBIFS reserves thrice as many LEBs as the index
+ * would take if it was consolidated and written to the flash. This guarantees
+ * that the "in-the-gaps" commit method always succeeds and UBIFS will always
+ * be able to commit dirty index. So this function basically adds amount of
+ * budgeted index space to the size of the current index, multiplies this by 3,
+ * and makes sure this does not exceed the amount of free LEBs.
+ *
+ * Notes about @c->bi.min_idx_lebs and @c->lst.idx_lebs variables:
+ * o @c->lst.idx_lebs is the number of LEBs the index currently uses. It might
+ * be large, because UBIFS does not do any index consolidation as long as
+ * there is free space. IOW, the index may take a lot of LEBs, but the LEBs
+ * will contain a lot of dirt.
+ * o @c->bi.min_idx_lebs is the number of LEBS the index presumably takes. IOW,
+ * the index may be consolidated to take up to @c->bi.min_idx_lebs LEBs.
+ *
+ * This function returns zero in case of success, and %-ENOSPC in case of
+ * failure.
+ */
+static int do_budget_space(struct ubifs_info *c)
+{
+ long long outstanding, available;
+ int lebs, rsvd_idx_lebs, min_idx_lebs;
+
+ /* First budget index space */
+ min_idx_lebs = ubifs_calc_min_idx_lebs(c);
+
+ /* Now 'min_idx_lebs' contains number of LEBs to reserve */
+ if (min_idx_lebs > c->lst.idx_lebs)
+ rsvd_idx_lebs = min_idx_lebs - c->lst.idx_lebs;
+ else
+ rsvd_idx_lebs = 0;
+
+ /*
+ * The number of LEBs that are available to be used by the index is:
+ *
+ * @c->lst.empty_lebs + @c->freeable_cnt + @c->idx_gc_cnt -
+ * @c->lst.taken_empty_lebs
+ *
+ * @c->lst.empty_lebs are available because they are empty.
+ * @c->freeable_cnt are available because they contain only free and
+ * dirty space, @c->idx_gc_cnt are available because they are index
+ * LEBs that have been garbage collected and are awaiting the commit
+ * before they can be used. And the in-the-gaps method will grab these
+ * if it needs them. @c->lst.taken_empty_lebs are empty LEBs that have
+ * already been allocated for some purpose.
+ *
+ * Note, @c->idx_gc_cnt is included to both @c->lst.empty_lebs (because
+ * these LEBs are empty) and to @c->lst.taken_empty_lebs (because they
+ * are taken until after the commit).
+ *
+ * Note, @c->lst.taken_empty_lebs may temporarily be higher by one
+ * because of the way we serialize LEB allocations and budgeting. See a
+ * comment in 'ubifs_find_free_space()'.
+ */
+ lebs = c->lst.empty_lebs + c->freeable_cnt + c->idx_gc_cnt -
+ c->lst.taken_empty_lebs;
+ if (unlikely(rsvd_idx_lebs > lebs)) {
+ dbg_budg("out of indexing space: min_idx_lebs %d (old %d), rsvd_idx_lebs %d",
+ min_idx_lebs, c->bi.min_idx_lebs, rsvd_idx_lebs);
+ return -ENOSPC;
+ }
+
+ available = ubifs_calc_available(c, min_idx_lebs);
+ outstanding = c->bi.data_growth + c->bi.dd_growth;
+
+ if (unlikely(available < outstanding)) {
+ dbg_budg("out of data space: available %lld, outstanding %lld",
+ available, outstanding);
+ return -ENOSPC;
+ }
+
+ if (available - outstanding <= c->rp_size && !can_use_rp(c))
+ return -ENOSPC;
+
+ c->bi.min_idx_lebs = min_idx_lebs;
+ return 0;
+}
+
+/**
+ * calc_idx_growth - calculate approximate index growth from budgeting request.
+ * @c: UBIFS file-system description object
+ * @req: budgeting request
+ *
+ * For now we assume each new node adds one znode. But this is rather poor
+ * approximation, though.
+ */
+static int calc_idx_growth(const struct ubifs_info *c,
+ const struct ubifs_budget_req *req)
+{
+ int znodes;
+
+ znodes = req->new_ino + (req->new_page << UBIFS_BLOCKS_PER_PAGE_SHIFT) +
+ req->new_dent;
+ return znodes * c->max_idx_node_sz;
+}
+
+/**
+ * calc_data_growth - calculate approximate amount of new data from budgeting
+ * request.
+ * @c: UBIFS file-system description object
+ * @req: budgeting request
+ */
+static int calc_data_growth(const struct ubifs_info *c,
+ const struct ubifs_budget_req *req)
+{
+ int data_growth;
+
+ data_growth = req->new_ino ? c->bi.inode_budget : 0;
+ if (req->new_page)
+ data_growth += c->bi.page_budget;
+ if (req->new_dent)
+ data_growth += c->bi.dent_budget;
+ data_growth += req->new_ino_d;
+ return data_growth;
+}
+
+/**
+ * calc_dd_growth - calculate approximate amount of data which makes other data
+ * dirty from budgeting request.
+ * @c: UBIFS file-system description object
+ * @req: budgeting request
+ */
+static int calc_dd_growth(const struct ubifs_info *c,
+ const struct ubifs_budget_req *req)
+{
+ int dd_growth;
+
+ dd_growth = req->dirtied_page ? c->bi.page_budget : 0;
+
+ if (req->dirtied_ino)
+ dd_growth += c->bi.inode_budget << (req->dirtied_ino - 1);
+ if (req->mod_dent)
+ dd_growth += c->bi.dent_budget;
+ dd_growth += req->dirtied_ino_d;
+ return dd_growth;
+}
+
+/**
+ * ubifs_budget_space - ensure there is enough space to complete an operation.
+ * @c: UBIFS file-system description object
+ * @req: budget request
+ *
+ * This function allocates budget for an operation. It uses pessimistic
+ * approximation of how much flash space the operation needs. The goal of this
+ * function is to make sure UBIFS always has flash space to flush all dirty
+ * pages, dirty inodes, and dirty znodes (liability). This function may force
+ * commit, garbage-collection or write-back. Returns zero in case of success,
+ * %-ENOSPC if there is no free space and other negative error codes in case of
+ * failures.
+ */
+int ubifs_budget_space(struct ubifs_info *c, struct ubifs_budget_req *req)
+{
+ int err, idx_growth, data_growth, dd_growth, retried = 0;
+
+ ubifs_assert(req->new_page <= 1);
+ ubifs_assert(req->dirtied_page <= 1);
+ ubifs_assert(req->new_dent <= 1);
+ ubifs_assert(req->mod_dent <= 1);
+ ubifs_assert(req->new_ino <= 1);
+ ubifs_assert(req->new_ino_d <= UBIFS_MAX_INO_DATA);
+ ubifs_assert(req->dirtied_ino <= 4);
+ ubifs_assert(req->dirtied_ino_d <= UBIFS_MAX_INO_DATA * 4);
+ ubifs_assert(!(req->new_ino_d & 7));
+ ubifs_assert(!(req->dirtied_ino_d & 7));
+
+ data_growth = calc_data_growth(c, req);
+ dd_growth = calc_dd_growth(c, req);
+ if (!data_growth && !dd_growth)
+ return 0;
+ idx_growth = calc_idx_growth(c, req);
+
+again:
+ spin_lock(&c->space_lock);
+ ubifs_assert(c->bi.idx_growth >= 0);
+ ubifs_assert(c->bi.data_growth >= 0);
+ ubifs_assert(c->bi.dd_growth >= 0);
+
+ if (unlikely(c->bi.nospace) && (c->bi.nospace_rp || !can_use_rp(c))) {
+ dbg_budg("no space");
+ spin_unlock(&c->space_lock);
+ return -ENOSPC;
+ }
+
+ c->bi.idx_growth += idx_growth;
+ c->bi.data_growth += data_growth;
+ c->bi.dd_growth += dd_growth;
+
+ err = do_budget_space(c);
+ if (likely(!err)) {
+ req->idx_growth = idx_growth;
+ req->data_growth = data_growth;
+ req->dd_growth = dd_growth;
+ spin_unlock(&c->space_lock);
+ return 0;
+ }
+
+ /* Restore the old values */
+ c->bi.idx_growth -= idx_growth;
+ c->bi.data_growth -= data_growth;
+ c->bi.dd_growth -= dd_growth;
+ spin_unlock(&c->space_lock);
+
+ if (req->fast) {
+ dbg_budg("no space for fast budgeting");
+ return err;
+ }
+
+ err = make_free_space(c);
+ cond_resched();
+ if (err == -EAGAIN) {
+ dbg_budg("try again");
+ goto again;
+ } else if (err == -ENOSPC) {
+ if (!retried) {
+ retried = 1;
+ dbg_budg("-ENOSPC, but anyway try once again");
+ goto again;
+ }
+ dbg_budg("FS is full, -ENOSPC");
+ c->bi.nospace = 1;
+ if (can_use_rp(c) || c->rp_size == 0)
+ c->bi.nospace_rp = 1;
+ smp_wmb();
+ } else
+ ubifs_err(c, "cannot budget space, error %d", err);
+ return err;
+}
+
+/**
+ * ubifs_release_budget - release budgeted free space.
+ * @c: UBIFS file-system description object
+ * @req: budget request
+ *
+ * This function releases the space budgeted by 'ubifs_budget_space()'. Note,
+ * since the index changes (which were budgeted for in @req->idx_growth) will
+ * only be written to the media on commit, this function moves the index budget
+ * from @c->bi.idx_growth to @c->bi.uncommitted_idx. The latter will be zeroed
+ * by the commit operation.
+ */
+void ubifs_release_budget(struct ubifs_info *c, struct ubifs_budget_req *req)
+{
+ ubifs_assert(req->new_page <= 1);
+ ubifs_assert(req->dirtied_page <= 1);
+ ubifs_assert(req->new_dent <= 1);
+ ubifs_assert(req->mod_dent <= 1);
+ ubifs_assert(req->new_ino <= 1);
+ ubifs_assert(req->new_ino_d <= UBIFS_MAX_INO_DATA);
+ ubifs_assert(req->dirtied_ino <= 4);
+ ubifs_assert(req->dirtied_ino_d <= UBIFS_MAX_INO_DATA * 4);
+ ubifs_assert(!(req->new_ino_d & 7));
+ ubifs_assert(!(req->dirtied_ino_d & 7));
+ if (!req->recalculate) {
+ ubifs_assert(req->idx_growth >= 0);
+ ubifs_assert(req->data_growth >= 0);
+ ubifs_assert(req->dd_growth >= 0);
+ }
+
+ if (req->recalculate) {
+ req->data_growth = calc_data_growth(c, req);
+ req->dd_growth = calc_dd_growth(c, req);
+ req->idx_growth = calc_idx_growth(c, req);
+ }
+
+ if (!req->data_growth && !req->dd_growth)
+ return;
+
+ c->bi.nospace = c->bi.nospace_rp = 0;
+ smp_wmb();
+
+ spin_lock(&c->space_lock);
+ c->bi.idx_growth -= req->idx_growth;
+ c->bi.uncommitted_idx += req->idx_growth;
+ c->bi.data_growth -= req->data_growth;
+ c->bi.dd_growth -= req->dd_growth;
+ c->bi.min_idx_lebs = ubifs_calc_min_idx_lebs(c);
+
+ ubifs_assert(c->bi.idx_growth >= 0);
+ ubifs_assert(c->bi.data_growth >= 0);
+ ubifs_assert(c->bi.dd_growth >= 0);
+ ubifs_assert(c->bi.min_idx_lebs < c->main_lebs);
+ ubifs_assert(!(c->bi.idx_growth & 7));
+ ubifs_assert(!(c->bi.data_growth & 7));
+ ubifs_assert(!(c->bi.dd_growth & 7));
+ spin_unlock(&c->space_lock);
+}
+
+/**
+ * ubifs_convert_page_budget - convert budget of a new page.
+ * @c: UBIFS file-system description object
+ *
+ * This function converts budget which was allocated for a new page of data to
+ * the budget of changing an existing page of data. The latter is smaller than
+ * the former, so this function only does simple re-calculation and does not
+ * involve any write-back.
+ */
+void ubifs_convert_page_budget(struct ubifs_info *c)
+{
+ spin_lock(&c->space_lock);
+ /* Release the index growth reservation */
+ c->bi.idx_growth -= c->max_idx_node_sz << UBIFS_BLOCKS_PER_PAGE_SHIFT;
+ /* Release the data growth reservation */
+ c->bi.data_growth -= c->bi.page_budget;
+ /* Increase the dirty data growth reservation instead */
+ c->bi.dd_growth += c->bi.page_budget;
+ /* And re-calculate the indexing space reservation */
+ c->bi.min_idx_lebs = ubifs_calc_min_idx_lebs(c);
+ spin_unlock(&c->space_lock);
+}
+
+/**
+ * ubifs_release_dirty_inode_budget - release dirty inode budget.
+ * @c: UBIFS file-system description object
+ * @ui: UBIFS inode to release the budget for
+ *
+ * This function releases budget corresponding to a dirty inode. It is usually
+ * called when after the inode has been written to the media and marked as
+ * clean. It also causes the "no space" flags to be cleared.
+ */
+void ubifs_release_dirty_inode_budget(struct ubifs_info *c,
+ struct ubifs_inode *ui)
+{
+ struct ubifs_budget_req req;
+
+ memset(&req, 0, sizeof(struct ubifs_budget_req));
+ /* The "no space" flags will be cleared because dd_growth is > 0 */
+ req.dd_growth = c->bi.inode_budget + ALIGN(ui->data_len, 8);
+ ubifs_release_budget(c, &req);
+}
+#endif
+
+/**
+ * ubifs_reported_space - calculate reported free space.
+ * @c: the UBIFS file-system description object
+ * @free: amount of free space
+ *
+ * This function calculates amount of free space which will be reported to
+ * user-space. User-space application tend to expect that if the file-system
+ * (e.g., via the 'statfs()' call) reports that it has N bytes available, they
+ * are able to write a file of size N. UBIFS attaches node headers to each data
+ * node and it has to write indexing nodes as well. This introduces additional
+ * overhead, and UBIFS has to report slightly less free space to meet the above
+ * expectations.
+ *
+ * This function assumes free space is made up of uncompressed data nodes and
+ * full index nodes (one per data node, tripled because we always allow enough
+ * space to write the index thrice).
+ *
+ * Note, the calculation is pessimistic, which means that most of the time
+ * UBIFS reports less space than it actually has.
+ */
+long long ubifs_reported_space(const struct ubifs_info *c, long long free)
+{
+ int divisor, factor, f;
+
+ /*
+ * Reported space size is @free * X, where X is UBIFS block size
+ * divided by UBIFS block size + all overhead one data block
+ * introduces. The overhead is the node header + indexing overhead.
+ *
+ * Indexing overhead calculations are based on the following formula:
+ * I = N/(f - 1) + 1, where I - number of indexing nodes, N - number
+ * of data nodes, f - fanout. Because effective UBIFS fanout is twice
+ * as less than maximum fanout, we assume that each data node
+ * introduces 3 * @c->max_idx_node_sz / (@c->fanout/2 - 1) bytes.
+ * Note, the multiplier 3 is because UBIFS reserves thrice as more space
+ * for the index.
+ */
+ f = c->fanout > 3 ? c->fanout >> 1 : 2;
+ factor = UBIFS_BLOCK_SIZE;
+ divisor = UBIFS_MAX_DATA_NODE_SZ;
+ divisor += (c->max_idx_node_sz * 3) / (f - 1);
+ free *= factor;
+ return div_u64(free, divisor);
+}
+
+#ifndef __UBOOT__
+/**
+ * ubifs_get_free_space_nolock - return amount of free space.
+ * @c: UBIFS file-system description object
+ *
+ * This function calculates amount of free space to report to user-space.
+ *
+ * Because UBIFS may introduce substantial overhead (the index, node headers,
+ * alignment, wastage at the end of LEBs, etc), it cannot report real amount of
+ * free flash space it has (well, because not all dirty space is reclaimable,
+ * UBIFS does not actually know the real amount). If UBIFS did so, it would
+ * bread user expectations about what free space is. Users seem to accustomed
+ * to assume that if the file-system reports N bytes of free space, they would
+ * be able to fit a file of N bytes to the FS. This almost works for
+ * traditional file-systems, because they have way less overhead than UBIFS.
+ * So, to keep users happy, UBIFS tries to take the overhead into account.
+ */
+long long ubifs_get_free_space_nolock(struct ubifs_info *c)
+{
+ int rsvd_idx_lebs, lebs;
+ long long available, outstanding, free;
+
+ ubifs_assert(c->bi.min_idx_lebs == ubifs_calc_min_idx_lebs(c));
+ outstanding = c->bi.data_growth + c->bi.dd_growth;
+ available = ubifs_calc_available(c, c->bi.min_idx_lebs);
+
+ /*
+ * When reporting free space to user-space, UBIFS guarantees that it is
+ * possible to write a file of free space size. This means that for
+ * empty LEBs we may use more precise calculations than
+ * 'ubifs_calc_available()' is using. Namely, we know that in empty
+ * LEBs we would waste only @c->leb_overhead bytes, not @c->dark_wm.
+ * Thus, amend the available space.
+ *
+ * Note, the calculations below are similar to what we have in
+ * 'do_budget_space()', so refer there for comments.
+ */
+ if (c->bi.min_idx_lebs > c->lst.idx_lebs)
+ rsvd_idx_lebs = c->bi.min_idx_lebs - c->lst.idx_lebs;
+ else
+ rsvd_idx_lebs = 0;
+ lebs = c->lst.empty_lebs + c->freeable_cnt + c->idx_gc_cnt -
+ c->lst.taken_empty_lebs;
+ lebs -= rsvd_idx_lebs;
+ available += lebs * (c->dark_wm - c->leb_overhead);
+
+ if (available > outstanding)
+ free = ubifs_reported_space(c, available - outstanding);
+ else
+ free = 0;
+ return free;
+}
+
+/**
+ * ubifs_get_free_space - return amount of free space.
+ * @c: UBIFS file-system description object
+ *
+ * This function calculates and returns amount of free space to report to
+ * user-space.
+ */
+long long ubifs_get_free_space(struct ubifs_info *c)
+{
+ long long free;
+
+ spin_lock(&c->space_lock);
+ free = ubifs_get_free_space_nolock(c);
+ spin_unlock(&c->space_lock);
+
+ return free;
+}
+#endif
diff --git a/roms/u-boot/fs/ubifs/crc16.c b/roms/u-boot/fs/ubifs/crc16.c
new file mode 100644
index 000000000..443ccf855
--- /dev/null
+++ b/roms/u-boot/fs/ubifs/crc16.c
@@ -0,0 +1,60 @@
+/*
+ * crc16.c
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#include <linux/types.h>
+#include "crc16.h"
+
+/** CRC table for the CRC-16. The poly is 0x8005 (x^16 + x^15 + x^2 + 1) */
+u16 const crc16_table[256] = {
+ 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
+ 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
+ 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
+ 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
+ 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
+ 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
+ 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
+ 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
+ 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
+ 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
+ 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
+ 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
+ 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
+ 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
+ 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
+ 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
+ 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
+ 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
+ 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
+ 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
+ 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
+ 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
+ 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
+ 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
+ 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
+ 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
+ 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
+ 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
+ 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
+ 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
+ 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
+ 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
+};
+
+/**
+ * crc16 - compute the CRC-16 for the data buffer
+ * @crc: previous CRC value
+ * @buffer: data pointer
+ * @len: number of bytes in the buffer
+ *
+ * Returns the updated CRC value.
+ */
+u16 crc16(u16 crc, u8 const *buffer, size_t len)
+{
+ while (len--)
+ crc = crc16_byte(crc, *buffer++);
+ return crc;
+}
diff --git a/roms/u-boot/fs/ubifs/crc16.h b/roms/u-boot/fs/ubifs/crc16.h
new file mode 100644
index 000000000..052fd3311
--- /dev/null
+++ b/roms/u-boot/fs/ubifs/crc16.h
@@ -0,0 +1,29 @@
+/*
+ * crc16.h - CRC-16 routine
+ *
+ * Implements the standard CRC-16:
+ * Width 16
+ * Poly 0x8005 (x^16 + x^15 + x^2 + 1)
+ * Init 0
+ *
+ * Copyright (c) 2005 Ben Gardner <bgardner@wabtec.com>
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#ifndef __CRC16_H
+#define __CRC16_H
+
+#include <linux/types.h>
+
+extern u16 const crc16_table[256];
+
+extern u16 crc16(u16 crc, const u8 *buffer, size_t len);
+
+static inline u16 crc16_byte(u16 crc, const u8 data)
+{
+ return (crc >> 8) ^ crc16_table[(crc ^ data) & 0xff];
+}
+
+#endif /* __CRC16_H */
diff --git a/roms/u-boot/fs/ubifs/debug.c b/roms/u-boot/fs/ubifs/debug.c
new file mode 100644
index 000000000..2ff8f1a5f
--- /dev/null
+++ b/roms/u-boot/fs/ubifs/debug.c
@@ -0,0 +1,3151 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation
+ *
+ * Authors: Artem Bityutskiy (Битюцкий Артём)
+ * Adrian Hunter
+ */
+
+/*
+ * This file implements most of the debugging stuff which is compiled in only
+ * when it is enabled. But some debugging check functions are implemented in
+ * corresponding subsystem, just because they are closely related and utilize
+ * various local functions of those subsystems.
+ */
+
+#include <hexdump.h>
+#include <log.h>
+#include <dm/devres.h>
+
+#ifndef __UBOOT__
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/math64.h>
+#include <linux/uaccess.h>
+#include <linux/random.h>
+#else
+#include <linux/compat.h>
+#include <linux/err.h>
+#endif
+#include "ubifs.h"
+
+#ifndef __UBOOT__
+static DEFINE_SPINLOCK(dbg_lock);
+#endif
+
+static const char *get_key_fmt(int fmt)
+{
+ switch (fmt) {
+ case UBIFS_SIMPLE_KEY_FMT:
+ return "simple";
+ default:
+ return "unknown/invalid format";
+ }
+}
+
+static const char *get_key_hash(int hash)
+{
+ switch (hash) {
+ case UBIFS_KEY_HASH_R5:
+ return "R5";
+ case UBIFS_KEY_HASH_TEST:
+ return "test";
+ default:
+ return "unknown/invalid name hash";
+ }
+}
+
+static const char *get_key_type(int type)
+{
+ switch (type) {
+ case UBIFS_INO_KEY:
+ return "inode";
+ case UBIFS_DENT_KEY:
+ return "direntry";
+ case UBIFS_XENT_KEY:
+ return "xentry";
+ case UBIFS_DATA_KEY:
+ return "data";
+ case UBIFS_TRUN_KEY:
+ return "truncate";
+ default:
+ return "unknown/invalid key";
+ }
+}
+
+#ifndef __UBOOT__
+static const char *get_dent_type(int type)
+{
+ switch (type) {
+ case UBIFS_ITYPE_REG:
+ return "file";
+ case UBIFS_ITYPE_DIR:
+ return "dir";
+ case UBIFS_ITYPE_LNK:
+ return "symlink";
+ case UBIFS_ITYPE_BLK:
+ return "blkdev";
+ case UBIFS_ITYPE_CHR:
+ return "char dev";
+ case UBIFS_ITYPE_FIFO:
+ return "fifo";
+ case UBIFS_ITYPE_SOCK:
+ return "socket";
+ default:
+ return "unknown/invalid type";
+ }
+}
+#endif
+
+const char *dbg_snprintf_key(const struct ubifs_info *c,
+ const union ubifs_key *key, char *buffer, int len)
+{
+ char *p = buffer;
+ int type = key_type(c, key);
+
+ if (c->key_fmt == UBIFS_SIMPLE_KEY_FMT) {
+ switch (type) {
+ case UBIFS_INO_KEY:
+ len -= snprintf(p, len, "(%lu, %s)",
+ (unsigned long)key_inum(c, key),
+ get_key_type(type));
+ break;
+ case UBIFS_DENT_KEY:
+ case UBIFS_XENT_KEY:
+ len -= snprintf(p, len, "(%lu, %s, %#08x)",
+ (unsigned long)key_inum(c, key),
+ get_key_type(type), key_hash(c, key));
+ break;
+ case UBIFS_DATA_KEY:
+ len -= snprintf(p, len, "(%lu, %s, %u)",
+ (unsigned long)key_inum(c, key),
+ get_key_type(type), key_block(c, key));
+ break;
+ case UBIFS_TRUN_KEY:
+ len -= snprintf(p, len, "(%lu, %s)",
+ (unsigned long)key_inum(c, key),
+ get_key_type(type));
+ break;
+ default:
+ len -= snprintf(p, len, "(bad key type: %#08x, %#08x)",
+ key->u32[0], key->u32[1]);
+ }
+ } else
+ len -= snprintf(p, len, "bad key format %d", c->key_fmt);
+ ubifs_assert(len > 0);
+ return p;
+}
+
+const char *dbg_ntype(int type)
+{
+ switch (type) {
+ case UBIFS_PAD_NODE:
+ return "padding node";
+ case UBIFS_SB_NODE:
+ return "superblock node";
+ case UBIFS_MST_NODE:
+ return "master node";
+ case UBIFS_REF_NODE:
+ return "reference node";
+ case UBIFS_INO_NODE:
+ return "inode node";
+ case UBIFS_DENT_NODE:
+ return "direntry node";
+ case UBIFS_XENT_NODE:
+ return "xentry node";
+ case UBIFS_DATA_NODE:
+ return "data node";
+ case UBIFS_TRUN_NODE:
+ return "truncate node";
+ case UBIFS_IDX_NODE:
+ return "indexing node";
+ case UBIFS_CS_NODE:
+ return "commit start node";
+ case UBIFS_ORPH_NODE:
+ return "orphan node";
+ default:
+ return "unknown node";
+ }
+}
+
+static const char *dbg_gtype(int type)
+{
+ switch (type) {
+ case UBIFS_NO_NODE_GROUP:
+ return "no node group";
+ case UBIFS_IN_NODE_GROUP:
+ return "in node group";
+ case UBIFS_LAST_OF_NODE_GROUP:
+ return "last of node group";
+ default:
+ return "unknown";
+ }
+}
+
+const char *dbg_cstate(int cmt_state)
+{
+ switch (cmt_state) {
+ case COMMIT_RESTING:
+ return "commit resting";
+ case COMMIT_BACKGROUND:
+ return "background commit requested";
+ case COMMIT_REQUIRED:
+ return "commit required";
+ case COMMIT_RUNNING_BACKGROUND:
+ return "BACKGROUND commit running";
+ case COMMIT_RUNNING_REQUIRED:
+ return "commit running and required";
+ case COMMIT_BROKEN:
+ return "broken commit";
+ default:
+ return "unknown commit state";
+ }
+}
+
+const char *dbg_jhead(int jhead)
+{
+ switch (jhead) {
+ case GCHD:
+ return "0 (GC)";
+ case BASEHD:
+ return "1 (base)";
+ case DATAHD:
+ return "2 (data)";
+ default:
+ return "unknown journal head";
+ }
+}
+
+static void dump_ch(const struct ubifs_ch *ch)
+{
+ pr_err("\tmagic %#x\n", le32_to_cpu(ch->magic));
+ pr_err("\tcrc %#x\n", le32_to_cpu(ch->crc));
+ pr_err("\tnode_type %d (%s)\n", ch->node_type,
+ dbg_ntype(ch->node_type));
+ pr_err("\tgroup_type %d (%s)\n", ch->group_type,
+ dbg_gtype(ch->group_type));
+ pr_err("\tsqnum %llu\n",
+ (unsigned long long)le64_to_cpu(ch->sqnum));
+ pr_err("\tlen %u\n", le32_to_cpu(ch->len));
+}
+
+void ubifs_dump_inode(struct ubifs_info *c, const struct inode *inode)
+{
+#ifndef __UBOOT__
+ const struct ubifs_inode *ui = ubifs_inode(inode);
+ struct qstr nm = { .name = NULL };
+ union ubifs_key key;
+ struct ubifs_dent_node *dent, *pdent = NULL;
+ int count = 2;
+
+ pr_err("Dump in-memory inode:");
+ pr_err("\tinode %lu\n", inode->i_ino);
+ pr_err("\tsize %llu\n",
+ (unsigned long long)i_size_read(inode));
+ pr_err("\tnlink %u\n", inode->i_nlink);
+ pr_err("\tuid %u\n", (unsigned int)i_uid_read(inode));
+ pr_err("\tgid %u\n", (unsigned int)i_gid_read(inode));
+ pr_err("\tatime %u.%u\n",
+ (unsigned int)inode->i_atime.tv_sec,
+ (unsigned int)inode->i_atime.tv_nsec);
+ pr_err("\tmtime %u.%u\n",
+ (unsigned int)inode->i_mtime.tv_sec,
+ (unsigned int)inode->i_mtime.tv_nsec);
+ pr_err("\tctime %u.%u\n",
+ (unsigned int)inode->i_ctime.tv_sec,
+ (unsigned int)inode->i_ctime.tv_nsec);
+ pr_err("\tcreat_sqnum %llu\n", ui->creat_sqnum);
+ pr_err("\txattr_size %u\n", ui->xattr_size);
+ pr_err("\txattr_cnt %u\n", ui->xattr_cnt);
+ pr_err("\txattr_names %u\n", ui->xattr_names);
+ pr_err("\tdirty %u\n", ui->dirty);
+ pr_err("\txattr %u\n", ui->xattr);
+ pr_err("\tbulk_read %u\n", ui->xattr);
+ pr_err("\tsynced_i_size %llu\n",
+ (unsigned long long)ui->synced_i_size);
+ pr_err("\tui_size %llu\n",
+ (unsigned long long)ui->ui_size);
+ pr_err("\tflags %d\n", ui->flags);
+ pr_err("\tcompr_type %d\n", ui->compr_type);
+ pr_err("\tlast_page_read %lu\n", ui->last_page_read);
+ pr_err("\tread_in_a_row %lu\n", ui->read_in_a_row);
+ pr_err("\tdata_len %d\n", ui->data_len);
+
+ if (!S_ISDIR(inode->i_mode))
+ return;
+
+ pr_err("List of directory entries:\n");
+ ubifs_assert(!mutex_is_locked(&c->tnc_mutex));
+
+ lowest_dent_key(c, &key, inode->i_ino);
+ while (1) {
+ dent = ubifs_tnc_next_ent(c, &key, &nm);
+ if (IS_ERR(dent)) {
+ if (PTR_ERR(dent) != -ENOENT)
+ pr_err("error %ld\n", PTR_ERR(dent));
+ break;
+ }
+
+ pr_err("\t%d: %s (%s)\n",
+ count++, dent->name, get_dent_type(dent->type));
+
+ nm.name = dent->name;
+ nm.len = le16_to_cpu(dent->nlen);
+ kfree(pdent);
+ pdent = dent;
+ key_read(c, &dent->key, &key);
+ }
+ kfree(pdent);
+#endif
+}
+
+void ubifs_dump_node(const struct ubifs_info *c, const void *node)
+{
+ int i, n;
+ union ubifs_key key;
+ const struct ubifs_ch *ch = node;
+ char key_buf[DBG_KEY_BUF_LEN];
+
+ /* If the magic is incorrect, just hexdump the first bytes */
+ if (le32_to_cpu(ch->magic) != UBIFS_NODE_MAGIC) {
+ pr_err("Not a node, first %zu bytes:", UBIFS_CH_SZ);
+ print_hex_dump("", DUMP_PREFIX_OFFSET, 32, 1,
+ (void *)node, UBIFS_CH_SZ, 1);
+ return;
+ }
+
+ spin_lock(&dbg_lock);
+ dump_ch(node);
+
+ switch (ch->node_type) {
+ case UBIFS_PAD_NODE:
+ {
+ const struct ubifs_pad_node *pad = node;
+
+ pr_err("\tpad_len %u\n", le32_to_cpu(pad->pad_len));
+ break;
+ }
+ case UBIFS_SB_NODE:
+ {
+ const struct ubifs_sb_node *sup = node;
+ unsigned int sup_flags = le32_to_cpu(sup->flags);
+
+ pr_err("\tkey_hash %d (%s)\n",
+ (int)sup->key_hash, get_key_hash(sup->key_hash));
+ pr_err("\tkey_fmt %d (%s)\n",
+ (int)sup->key_fmt, get_key_fmt(sup->key_fmt));
+ pr_err("\tflags %#x\n", sup_flags);
+ pr_err("\tbig_lpt %u\n",
+ !!(sup_flags & UBIFS_FLG_BIGLPT));
+ pr_err("\tspace_fixup %u\n",
+ !!(sup_flags & UBIFS_FLG_SPACE_FIXUP));
+ pr_err("\tmin_io_size %u\n", le32_to_cpu(sup->min_io_size));
+ pr_err("\tleb_size %u\n", le32_to_cpu(sup->leb_size));
+ pr_err("\tleb_cnt %u\n", le32_to_cpu(sup->leb_cnt));
+ pr_err("\tmax_leb_cnt %u\n", le32_to_cpu(sup->max_leb_cnt));
+ pr_err("\tmax_bud_bytes %llu\n",
+ (unsigned long long)le64_to_cpu(sup->max_bud_bytes));
+ pr_err("\tlog_lebs %u\n", le32_to_cpu(sup->log_lebs));
+ pr_err("\tlpt_lebs %u\n", le32_to_cpu(sup->lpt_lebs));
+ pr_err("\torph_lebs %u\n", le32_to_cpu(sup->orph_lebs));
+ pr_err("\tjhead_cnt %u\n", le32_to_cpu(sup->jhead_cnt));
+ pr_err("\tfanout %u\n", le32_to_cpu(sup->fanout));
+ pr_err("\tlsave_cnt %u\n", le32_to_cpu(sup->lsave_cnt));
+ pr_err("\tdefault_compr %u\n",
+ (int)le16_to_cpu(sup->default_compr));
+ pr_err("\trp_size %llu\n",
+ (unsigned long long)le64_to_cpu(sup->rp_size));
+ pr_err("\trp_uid %u\n", le32_to_cpu(sup->rp_uid));
+ pr_err("\trp_gid %u\n", le32_to_cpu(sup->rp_gid));
+ pr_err("\tfmt_version %u\n", le32_to_cpu(sup->fmt_version));
+ pr_err("\ttime_gran %u\n", le32_to_cpu(sup->time_gran));
+ pr_err("\tUUID %pUB\n", sup->uuid);
+ break;
+ }
+ case UBIFS_MST_NODE:
+ {
+ const struct ubifs_mst_node *mst = node;
+
+ pr_err("\thighest_inum %llu\n",
+ (unsigned long long)le64_to_cpu(mst->highest_inum));
+ pr_err("\tcommit number %llu\n",
+ (unsigned long long)le64_to_cpu(mst->cmt_no));
+ pr_err("\tflags %#x\n", le32_to_cpu(mst->flags));
+ pr_err("\tlog_lnum %u\n", le32_to_cpu(mst->log_lnum));
+ pr_err("\troot_lnum %u\n", le32_to_cpu(mst->root_lnum));
+ pr_err("\troot_offs %u\n", le32_to_cpu(mst->root_offs));
+ pr_err("\troot_len %u\n", le32_to_cpu(mst->root_len));
+ pr_err("\tgc_lnum %u\n", le32_to_cpu(mst->gc_lnum));
+ pr_err("\tihead_lnum %u\n", le32_to_cpu(mst->ihead_lnum));
+ pr_err("\tihead_offs %u\n", le32_to_cpu(mst->ihead_offs));
+ pr_err("\tindex_size %llu\n",
+ (unsigned long long)le64_to_cpu(mst->index_size));
+ pr_err("\tlpt_lnum %u\n", le32_to_cpu(mst->lpt_lnum));
+ pr_err("\tlpt_offs %u\n", le32_to_cpu(mst->lpt_offs));
+ pr_err("\tnhead_lnum %u\n", le32_to_cpu(mst->nhead_lnum));
+ pr_err("\tnhead_offs %u\n", le32_to_cpu(mst->nhead_offs));
+ pr_err("\tltab_lnum %u\n", le32_to_cpu(mst->ltab_lnum));
+ pr_err("\tltab_offs %u\n", le32_to_cpu(mst->ltab_offs));
+ pr_err("\tlsave_lnum %u\n", le32_to_cpu(mst->lsave_lnum));
+ pr_err("\tlsave_offs %u\n", le32_to_cpu(mst->lsave_offs));
+ pr_err("\tlscan_lnum %u\n", le32_to_cpu(mst->lscan_lnum));
+ pr_err("\tleb_cnt %u\n", le32_to_cpu(mst->leb_cnt));
+ pr_err("\tempty_lebs %u\n", le32_to_cpu(mst->empty_lebs));
+ pr_err("\tidx_lebs %u\n", le32_to_cpu(mst->idx_lebs));
+ pr_err("\ttotal_free %llu\n",
+ (unsigned long long)le64_to_cpu(mst->total_free));
+ pr_err("\ttotal_dirty %llu\n",
+ (unsigned long long)le64_to_cpu(mst->total_dirty));
+ pr_err("\ttotal_used %llu\n",
+ (unsigned long long)le64_to_cpu(mst->total_used));
+ pr_err("\ttotal_dead %llu\n",
+ (unsigned long long)le64_to_cpu(mst->total_dead));
+ pr_err("\ttotal_dark %llu\n",
+ (unsigned long long)le64_to_cpu(mst->total_dark));
+ break;
+ }
+ case UBIFS_REF_NODE:
+ {
+ const struct ubifs_ref_node *ref = node;
+
+ pr_err("\tlnum %u\n", le32_to_cpu(ref->lnum));
+ pr_err("\toffs %u\n", le32_to_cpu(ref->offs));
+ pr_err("\tjhead %u\n", le32_to_cpu(ref->jhead));
+ break;
+ }
+ case UBIFS_INO_NODE:
+ {
+ const struct ubifs_ino_node *ino = node;
+
+ key_read(c, &ino->key, &key);
+ pr_err("\tkey %s\n",
+ dbg_snprintf_key(c, &key, key_buf, DBG_KEY_BUF_LEN));
+ pr_err("\tcreat_sqnum %llu\n",
+ (unsigned long long)le64_to_cpu(ino->creat_sqnum));
+ pr_err("\tsize %llu\n",
+ (unsigned long long)le64_to_cpu(ino->size));
+ pr_err("\tnlink %u\n", le32_to_cpu(ino->nlink));
+ pr_err("\tatime %lld.%u\n",
+ (long long)le64_to_cpu(ino->atime_sec),
+ le32_to_cpu(ino->atime_nsec));
+ pr_err("\tmtime %lld.%u\n",
+ (long long)le64_to_cpu(ino->mtime_sec),
+ le32_to_cpu(ino->mtime_nsec));
+ pr_err("\tctime %lld.%u\n",
+ (long long)le64_to_cpu(ino->ctime_sec),
+ le32_to_cpu(ino->ctime_nsec));
+ pr_err("\tuid %u\n", le32_to_cpu(ino->uid));
+ pr_err("\tgid %u\n", le32_to_cpu(ino->gid));
+ pr_err("\tmode %u\n", le32_to_cpu(ino->mode));
+ pr_err("\tflags %#x\n", le32_to_cpu(ino->flags));
+ pr_err("\txattr_cnt %u\n", le32_to_cpu(ino->xattr_cnt));
+ pr_err("\txattr_size %u\n", le32_to_cpu(ino->xattr_size));
+ pr_err("\txattr_names %u\n", le32_to_cpu(ino->xattr_names));
+ pr_err("\tcompr_type %#x\n",
+ (int)le16_to_cpu(ino->compr_type));
+ pr_err("\tdata len %u\n", le32_to_cpu(ino->data_len));
+ break;
+ }
+ case UBIFS_DENT_NODE:
+ case UBIFS_XENT_NODE:
+ {
+ const struct ubifs_dent_node *dent = node;
+ int nlen = le16_to_cpu(dent->nlen);
+
+ key_read(c, &dent->key, &key);
+ pr_err("\tkey %s\n",
+ dbg_snprintf_key(c, &key, key_buf, DBG_KEY_BUF_LEN));
+ pr_err("\tinum %llu\n",
+ (unsigned long long)le64_to_cpu(dent->inum));
+ pr_err("\ttype %d\n", (int)dent->type);
+ pr_err("\tnlen %d\n", nlen);
+ pr_err("\tname ");
+
+ if (nlen > UBIFS_MAX_NLEN)
+ pr_err("(bad name length, not printing, bad or corrupted node)");
+ else {
+ for (i = 0; i < nlen && dent->name[i]; i++)
+ pr_cont("%c", dent->name[i]);
+ }
+ pr_cont("\n");
+
+ break;
+ }
+ case UBIFS_DATA_NODE:
+ {
+ const struct ubifs_data_node *dn = node;
+ int dlen = le32_to_cpu(ch->len) - UBIFS_DATA_NODE_SZ;
+
+ key_read(c, &dn->key, &key);
+ pr_err("\tkey %s\n",
+ dbg_snprintf_key(c, &key, key_buf, DBG_KEY_BUF_LEN));
+ pr_err("\tsize %u\n", le32_to_cpu(dn->size));
+ pr_err("\tcompr_typ %d\n",
+ (int)le16_to_cpu(dn->compr_type));
+ pr_err("\tdata size %d\n", dlen);
+ pr_err("\tdata:\n");
+ print_hex_dump("\t", DUMP_PREFIX_OFFSET, 32, 1,
+ (void *)&dn->data, dlen, 0);
+ break;
+ }
+ case UBIFS_TRUN_NODE:
+ {
+ const struct ubifs_trun_node *trun = node;
+
+ pr_err("\tinum %u\n", le32_to_cpu(trun->inum));
+ pr_err("\told_size %llu\n",
+ (unsigned long long)le64_to_cpu(trun->old_size));
+ pr_err("\tnew_size %llu\n",
+ (unsigned long long)le64_to_cpu(trun->new_size));
+ break;
+ }
+ case UBIFS_IDX_NODE:
+ {
+ const struct ubifs_idx_node *idx = node;
+
+ n = le16_to_cpu(idx->child_cnt);
+ pr_err("\tchild_cnt %d\n", n);
+ pr_err("\tlevel %d\n", (int)le16_to_cpu(idx->level));
+ pr_err("\tBranches:\n");
+
+ for (i = 0; i < n && i < c->fanout - 1; i++) {
+ const struct ubifs_branch *br;
+
+ br = ubifs_idx_branch(c, idx, i);
+ key_read(c, &br->key, &key);
+ pr_err("\t%d: LEB %d:%d len %d key %s\n",
+ i, le32_to_cpu(br->lnum), le32_to_cpu(br->offs),
+ le32_to_cpu(br->len),
+ dbg_snprintf_key(c, &key, key_buf,
+ DBG_KEY_BUF_LEN));
+ }
+ break;
+ }
+ case UBIFS_CS_NODE:
+ break;
+ case UBIFS_ORPH_NODE:
+ {
+ const struct ubifs_orph_node *orph = node;
+
+ pr_err("\tcommit number %llu\n",
+ (unsigned long long)
+ le64_to_cpu(orph->cmt_no) & LLONG_MAX);
+ pr_err("\tlast node flag %llu\n",
+ (unsigned long long)(le64_to_cpu(orph->cmt_no)) >> 63);
+ n = (le32_to_cpu(ch->len) - UBIFS_ORPH_NODE_SZ) >> 3;
+ pr_err("\t%d orphan inode numbers:\n", n);
+ for (i = 0; i < n; i++)
+ pr_err("\t ino %llu\n",
+ (unsigned long long)le64_to_cpu(orph->inos[i]));
+ break;
+ }
+ default:
+ pr_err("node type %d was not recognized\n",
+ (int)ch->node_type);
+ }
+ spin_unlock(&dbg_lock);
+}
+
+void ubifs_dump_budget_req(const struct ubifs_budget_req *req)
+{
+ spin_lock(&dbg_lock);
+ pr_err("Budgeting request: new_ino %d, dirtied_ino %d\n",
+ req->new_ino, req->dirtied_ino);
+ pr_err("\tnew_ino_d %d, dirtied_ino_d %d\n",
+ req->new_ino_d, req->dirtied_ino_d);
+ pr_err("\tnew_page %d, dirtied_page %d\n",
+ req->new_page, req->dirtied_page);
+ pr_err("\tnew_dent %d, mod_dent %d\n",
+ req->new_dent, req->mod_dent);
+ pr_err("\tidx_growth %d\n", req->idx_growth);
+ pr_err("\tdata_growth %d dd_growth %d\n",
+ req->data_growth, req->dd_growth);
+ spin_unlock(&dbg_lock);
+}
+
+void ubifs_dump_lstats(const struct ubifs_lp_stats *lst)
+{
+ spin_lock(&dbg_lock);
+ pr_err("(pid %d) Lprops statistics: empty_lebs %d, idx_lebs %d\n",
+ current->pid, lst->empty_lebs, lst->idx_lebs);
+ pr_err("\ttaken_empty_lebs %d, total_free %lld, total_dirty %lld\n",
+ lst->taken_empty_lebs, lst->total_free, lst->total_dirty);
+ pr_err("\ttotal_used %lld, total_dark %lld, total_dead %lld\n",
+ lst->total_used, lst->total_dark, lst->total_dead);
+ spin_unlock(&dbg_lock);
+}
+
+#ifndef __UBOOT__
+void ubifs_dump_budg(struct ubifs_info *c, const struct ubifs_budg_info *bi)
+{
+ int i;
+ struct rb_node *rb;
+ struct ubifs_bud *bud;
+ struct ubifs_gced_idx_leb *idx_gc;
+ long long available, outstanding, free;
+
+ spin_lock(&c->space_lock);
+ spin_lock(&dbg_lock);
+ pr_err("(pid %d) Budgeting info: data budget sum %lld, total budget sum %lld\n",
+ current->pid, bi->data_growth + bi->dd_growth,
+ bi->data_growth + bi->dd_growth + bi->idx_growth);
+ pr_err("\tbudg_data_growth %lld, budg_dd_growth %lld, budg_idx_growth %lld\n",
+ bi->data_growth, bi->dd_growth, bi->idx_growth);
+ pr_err("\tmin_idx_lebs %d, old_idx_sz %llu, uncommitted_idx %lld\n",
+ bi->min_idx_lebs, bi->old_idx_sz, bi->uncommitted_idx);
+ pr_err("\tpage_budget %d, inode_budget %d, dent_budget %d\n",
+ bi->page_budget, bi->inode_budget, bi->dent_budget);
+ pr_err("\tnospace %u, nospace_rp %u\n", bi->nospace, bi->nospace_rp);
+ pr_err("\tdark_wm %d, dead_wm %d, max_idx_node_sz %d\n",
+ c->dark_wm, c->dead_wm, c->max_idx_node_sz);
+
+ if (bi != &c->bi)
+ /*
+ * If we are dumping saved budgeting data, do not print
+ * additional information which is about the current state, not
+ * the old one which corresponded to the saved budgeting data.
+ */
+ goto out_unlock;
+
+ pr_err("\tfreeable_cnt %d, calc_idx_sz %lld, idx_gc_cnt %d\n",
+ c->freeable_cnt, c->calc_idx_sz, c->idx_gc_cnt);
+ pr_err("\tdirty_pg_cnt %ld, dirty_zn_cnt %ld, clean_zn_cnt %ld\n",
+ atomic_long_read(&c->dirty_pg_cnt),
+ atomic_long_read(&c->dirty_zn_cnt),
+ atomic_long_read(&c->clean_zn_cnt));
+ pr_err("\tgc_lnum %d, ihead_lnum %d\n", c->gc_lnum, c->ihead_lnum);
+
+ /* If we are in R/O mode, journal heads do not exist */
+ if (c->jheads)
+ for (i = 0; i < c->jhead_cnt; i++)
+ pr_err("\tjhead %s\t LEB %d\n",
+ dbg_jhead(c->jheads[i].wbuf.jhead),
+ c->jheads[i].wbuf.lnum);
+ for (rb = rb_first(&c->buds); rb; rb = rb_next(rb)) {
+ bud = rb_entry(rb, struct ubifs_bud, rb);
+ pr_err("\tbud LEB %d\n", bud->lnum);
+ }
+ list_for_each_entry(bud, &c->old_buds, list)
+ pr_err("\told bud LEB %d\n", bud->lnum);
+ list_for_each_entry(idx_gc, &c->idx_gc, list)
+ pr_err("\tGC'ed idx LEB %d unmap %d\n",
+ idx_gc->lnum, idx_gc->unmap);
+ pr_err("\tcommit state %d\n", c->cmt_state);
+
+ /* Print budgeting predictions */
+ available = ubifs_calc_available(c, c->bi.min_idx_lebs);
+ outstanding = c->bi.data_growth + c->bi.dd_growth;
+ free = ubifs_get_free_space_nolock(c);
+ pr_err("Budgeting predictions:\n");
+ pr_err("\tavailable: %lld, outstanding %lld, free %lld\n",
+ available, outstanding, free);
+out_unlock:
+ spin_unlock(&dbg_lock);
+ spin_unlock(&c->space_lock);
+}
+#else
+void ubifs_dump_budg(struct ubifs_info *c, const struct ubifs_budg_info *bi)
+{
+}
+#endif
+
+void ubifs_dump_lprop(const struct ubifs_info *c, const struct ubifs_lprops *lp)
+{
+ int i, spc, dark = 0, dead = 0;
+ struct rb_node *rb;
+ struct ubifs_bud *bud;
+
+ spc = lp->free + lp->dirty;
+ if (spc < c->dead_wm)
+ dead = spc;
+ else
+ dark = ubifs_calc_dark(c, spc);
+
+ if (lp->flags & LPROPS_INDEX)
+ pr_err("LEB %-7d free %-8d dirty %-8d used %-8d free + dirty %-8d flags %#x (",
+ lp->lnum, lp->free, lp->dirty, c->leb_size - spc, spc,
+ lp->flags);
+ else
+ pr_err("LEB %-7d free %-8d dirty %-8d used %-8d free + dirty %-8d dark %-4d dead %-4d nodes fit %-3d flags %#-4x (",
+ lp->lnum, lp->free, lp->dirty, c->leb_size - spc, spc,
+ dark, dead, (int)(spc / UBIFS_MAX_NODE_SZ), lp->flags);
+
+ if (lp->flags & LPROPS_TAKEN) {
+ if (lp->flags & LPROPS_INDEX)
+ pr_cont("index, taken");
+ else
+ pr_cont("taken");
+ } else {
+ const char *s;
+
+ if (lp->flags & LPROPS_INDEX) {
+ switch (lp->flags & LPROPS_CAT_MASK) {
+ case LPROPS_DIRTY_IDX:
+ s = "dirty index";
+ break;
+ case LPROPS_FRDI_IDX:
+ s = "freeable index";
+ break;
+ default:
+ s = "index";
+ }
+ } else {
+ switch (lp->flags & LPROPS_CAT_MASK) {
+ case LPROPS_UNCAT:
+ s = "not categorized";
+ break;
+ case LPROPS_DIRTY:
+ s = "dirty";
+ break;
+ case LPROPS_FREE:
+ s = "free";
+ break;
+ case LPROPS_EMPTY:
+ s = "empty";
+ break;
+ case LPROPS_FREEABLE:
+ s = "freeable";
+ break;
+ default:
+ s = NULL;
+ break;
+ }
+ }
+ pr_cont("%s", s);
+ }
+
+ for (rb = rb_first((struct rb_root *)&c->buds); rb; rb = rb_next(rb)) {
+ bud = rb_entry(rb, struct ubifs_bud, rb);
+ if (bud->lnum == lp->lnum) {
+ int head = 0;
+ for (i = 0; i < c->jhead_cnt; i++) {
+ /*
+ * Note, if we are in R/O mode or in the middle
+ * of mounting/re-mounting, the write-buffers do
+ * not exist.
+ */
+ if (c->jheads &&
+ lp->lnum == c->jheads[i].wbuf.lnum) {
+ pr_cont(", jhead %s", dbg_jhead(i));
+ head = 1;
+ }
+ }
+ if (!head)
+ pr_cont(", bud of jhead %s",
+ dbg_jhead(bud->jhead));
+ }
+ }
+ if (lp->lnum == c->gc_lnum)
+ pr_cont(", GC LEB");
+ pr_cont(")\n");
+}
+
+void ubifs_dump_lprops(struct ubifs_info *c)
+{
+ int lnum, err;
+ struct ubifs_lprops lp;
+ struct ubifs_lp_stats lst;
+
+ pr_err("(pid %d) start dumping LEB properties\n", current->pid);
+ ubifs_get_lp_stats(c, &lst);
+ ubifs_dump_lstats(&lst);
+
+ for (lnum = c->main_first; lnum < c->leb_cnt; lnum++) {
+ err = ubifs_read_one_lp(c, lnum, &lp);
+ if (err) {
+ ubifs_err(c, "cannot read lprops for LEB %d", lnum);
+ continue;
+ }
+
+ ubifs_dump_lprop(c, &lp);
+ }
+ pr_err("(pid %d) finish dumping LEB properties\n", current->pid);
+}
+
+void ubifs_dump_lpt_info(struct ubifs_info *c)
+{
+ int i;
+
+ spin_lock(&dbg_lock);
+ pr_err("(pid %d) dumping LPT information\n", current->pid);
+ pr_err("\tlpt_sz: %lld\n", c->lpt_sz);
+ pr_err("\tpnode_sz: %d\n", c->pnode_sz);
+ pr_err("\tnnode_sz: %d\n", c->nnode_sz);
+ pr_err("\tltab_sz: %d\n", c->ltab_sz);
+ pr_err("\tlsave_sz: %d\n", c->lsave_sz);
+ pr_err("\tbig_lpt: %d\n", c->big_lpt);
+ pr_err("\tlpt_hght: %d\n", c->lpt_hght);
+ pr_err("\tpnode_cnt: %d\n", c->pnode_cnt);
+ pr_err("\tnnode_cnt: %d\n", c->nnode_cnt);
+ pr_err("\tdirty_pn_cnt: %d\n", c->dirty_pn_cnt);
+ pr_err("\tdirty_nn_cnt: %d\n", c->dirty_nn_cnt);
+ pr_err("\tlsave_cnt: %d\n", c->lsave_cnt);
+ pr_err("\tspace_bits: %d\n", c->space_bits);
+ pr_err("\tlpt_lnum_bits: %d\n", c->lpt_lnum_bits);
+ pr_err("\tlpt_offs_bits: %d\n", c->lpt_offs_bits);
+ pr_err("\tlpt_spc_bits: %d\n", c->lpt_spc_bits);
+ pr_err("\tpcnt_bits: %d\n", c->pcnt_bits);
+ pr_err("\tlnum_bits: %d\n", c->lnum_bits);
+ pr_err("\tLPT root is at %d:%d\n", c->lpt_lnum, c->lpt_offs);
+ pr_err("\tLPT head is at %d:%d\n",
+ c->nhead_lnum, c->nhead_offs);
+ pr_err("\tLPT ltab is at %d:%d\n", c->ltab_lnum, c->ltab_offs);
+ if (c->big_lpt)
+ pr_err("\tLPT lsave is at %d:%d\n",
+ c->lsave_lnum, c->lsave_offs);
+ for (i = 0; i < c->lpt_lebs; i++)
+ pr_err("\tLPT LEB %d free %d dirty %d tgc %d cmt %d\n",
+ i + c->lpt_first, c->ltab[i].free, c->ltab[i].dirty,
+ c->ltab[i].tgc, c->ltab[i].cmt);
+ spin_unlock(&dbg_lock);
+}
+
+void ubifs_dump_sleb(const struct ubifs_info *c,
+ const struct ubifs_scan_leb *sleb, int offs)
+{
+ struct ubifs_scan_node *snod;
+
+ pr_err("(pid %d) start dumping scanned data from LEB %d:%d\n",
+ current->pid, sleb->lnum, offs);
+
+ list_for_each_entry(snod, &sleb->nodes, list) {
+ cond_resched();
+ pr_err("Dumping node at LEB %d:%d len %d\n",
+ sleb->lnum, snod->offs, snod->len);
+ ubifs_dump_node(c, snod->node);
+ }
+}
+
+void ubifs_dump_leb(const struct ubifs_info *c, int lnum)
+{
+ struct ubifs_scan_leb *sleb;
+ struct ubifs_scan_node *snod;
+ void *buf;
+
+ pr_err("(pid %d) start dumping LEB %d\n", current->pid, lnum);
+
+ buf = __vmalloc(c->leb_size, GFP_NOFS, PAGE_KERNEL);
+ if (!buf) {
+ ubifs_err(c, "cannot allocate memory for dumping LEB %d", lnum);
+ return;
+ }
+
+ sleb = ubifs_scan(c, lnum, 0, buf, 0);
+ if (IS_ERR(sleb)) {
+ ubifs_err(c, "scan error %d", (int)PTR_ERR(sleb));
+ goto out;
+ }
+
+ pr_err("LEB %d has %d nodes ending at %d\n", lnum,
+ sleb->nodes_cnt, sleb->endpt);
+
+ list_for_each_entry(snod, &sleb->nodes, list) {
+ cond_resched();
+ pr_err("Dumping node at LEB %d:%d len %d\n", lnum,
+ snod->offs, snod->len);
+ ubifs_dump_node(c, snod->node);
+ }
+
+ pr_err("(pid %d) finish dumping LEB %d\n", current->pid, lnum);
+ ubifs_scan_destroy(sleb);
+
+out:
+ vfree(buf);
+ return;
+}
+
+void ubifs_dump_znode(const struct ubifs_info *c,
+ const struct ubifs_znode *znode)
+{
+ int n;
+ const struct ubifs_zbranch *zbr;
+ char key_buf[DBG_KEY_BUF_LEN];
+
+ spin_lock(&dbg_lock);
+ if (znode->parent)
+ zbr = &znode->parent->zbranch[znode->iip];
+ else
+ zbr = &c->zroot;
+
+ pr_err("znode %p, LEB %d:%d len %d parent %p iip %d level %d child_cnt %d flags %lx\n",
+ znode, zbr->lnum, zbr->offs, zbr->len, znode->parent, znode->iip,
+ znode->level, znode->child_cnt, znode->flags);
+
+ if (znode->child_cnt <= 0 || znode->child_cnt > c->fanout) {
+ spin_unlock(&dbg_lock);
+ return;
+ }
+
+ pr_err("zbranches:\n");
+ for (n = 0; n < znode->child_cnt; n++) {
+ zbr = &znode->zbranch[n];
+ if (znode->level > 0)
+ pr_err("\t%d: znode %p LEB %d:%d len %d key %s\n",
+ n, zbr->znode, zbr->lnum, zbr->offs, zbr->len,
+ dbg_snprintf_key(c, &zbr->key, key_buf,
+ DBG_KEY_BUF_LEN));
+ else
+ pr_err("\t%d: LNC %p LEB %d:%d len %d key %s\n",
+ n, zbr->znode, zbr->lnum, zbr->offs, zbr->len,
+ dbg_snprintf_key(c, &zbr->key, key_buf,
+ DBG_KEY_BUF_LEN));
+ }
+ spin_unlock(&dbg_lock);
+}
+
+void ubifs_dump_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap, int cat)
+{
+ int i;
+
+ pr_err("(pid %d) start dumping heap cat %d (%d elements)\n",
+ current->pid, cat, heap->cnt);
+ for (i = 0; i < heap->cnt; i++) {
+ struct ubifs_lprops *lprops = heap->arr[i];
+
+ pr_err("\t%d. LEB %d hpos %d free %d dirty %d flags %d\n",
+ i, lprops->lnum, lprops->hpos, lprops->free,
+ lprops->dirty, lprops->flags);
+ }
+ pr_err("(pid %d) finish dumping heap\n", current->pid);
+}
+
+void ubifs_dump_pnode(struct ubifs_info *c, struct ubifs_pnode *pnode,
+ struct ubifs_nnode *parent, int iip)
+{
+ int i;
+
+ pr_err("(pid %d) dumping pnode:\n", current->pid);
+ pr_err("\taddress %zx parent %zx cnext %zx\n",
+ (size_t)pnode, (size_t)parent, (size_t)pnode->cnext);
+ pr_err("\tflags %lu iip %d level %d num %d\n",
+ pnode->flags, iip, pnode->level, pnode->num);
+ for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
+ struct ubifs_lprops *lp = &pnode->lprops[i];
+
+ pr_err("\t%d: free %d dirty %d flags %d lnum %d\n",
+ i, lp->free, lp->dirty, lp->flags, lp->lnum);
+ }
+}
+
+void ubifs_dump_tnc(struct ubifs_info *c)
+{
+ struct ubifs_znode *znode;
+ int level;
+
+ pr_err("\n");
+ pr_err("(pid %d) start dumping TNC tree\n", current->pid);
+ znode = ubifs_tnc_levelorder_next(c->zroot.znode, NULL);
+ level = znode->level;
+ pr_err("== Level %d ==\n", level);
+ while (znode) {
+ if (level != znode->level) {
+ level = znode->level;
+ pr_err("== Level %d ==\n", level);
+ }
+ ubifs_dump_znode(c, znode);
+ znode = ubifs_tnc_levelorder_next(c->zroot.znode, znode);
+ }
+ pr_err("(pid %d) finish dumping TNC tree\n", current->pid);
+}
+
+static int dump_znode(struct ubifs_info *c, struct ubifs_znode *znode,
+ void *priv)
+{
+ ubifs_dump_znode(c, znode);
+ return 0;
+}
+
+/**
+ * ubifs_dump_index - dump the on-flash index.
+ * @c: UBIFS file-system description object
+ *
+ * This function dumps whole UBIFS indexing B-tree, unlike 'ubifs_dump_tnc()'
+ * which dumps only in-memory znodes and does not read znodes which from flash.
+ */
+void ubifs_dump_index(struct ubifs_info *c)
+{
+ dbg_walk_index(c, NULL, dump_znode, NULL);
+}
+
+#ifndef __UBOOT__
+/**
+ * dbg_save_space_info - save information about flash space.
+ * @c: UBIFS file-system description object
+ *
+ * This function saves information about UBIFS free space, dirty space, etc, in
+ * order to check it later.
+ */
+void dbg_save_space_info(struct ubifs_info *c)
+{
+ struct ubifs_debug_info *d = c->dbg;
+ int freeable_cnt;
+
+ spin_lock(&c->space_lock);
+ memcpy(&d->saved_lst, &c->lst, sizeof(struct ubifs_lp_stats));
+ memcpy(&d->saved_bi, &c->bi, sizeof(struct ubifs_budg_info));
+ d->saved_idx_gc_cnt = c->idx_gc_cnt;
+
+ /*
+ * We use a dirty hack here and zero out @c->freeable_cnt, because it
+ * affects the free space calculations, and UBIFS might not know about
+ * all freeable eraseblocks. Indeed, we know about freeable eraseblocks
+ * only when we read their lprops, and we do this only lazily, upon the
+ * need. So at any given point of time @c->freeable_cnt might be not
+ * exactly accurate.
+ *
+ * Just one example about the issue we hit when we did not zero
+ * @c->freeable_cnt.
+ * 1. The file-system is mounted R/O, c->freeable_cnt is %0. We save the
+ * amount of free space in @d->saved_free
+ * 2. We re-mount R/W, which makes UBIFS to read the "lsave"
+ * information from flash, where we cache LEBs from various
+ * categories ('ubifs_remount_fs()' -> 'ubifs_lpt_init()'
+ * -> 'lpt_init_wr()' -> 'read_lsave()' -> 'ubifs_lpt_lookup()'
+ * -> 'ubifs_get_pnode()' -> 'update_cats()'
+ * -> 'ubifs_add_to_cat()').
+ * 3. Lsave contains a freeable eraseblock, and @c->freeable_cnt
+ * becomes %1.
+ * 4. We calculate the amount of free space when the re-mount is
+ * finished in 'dbg_check_space_info()' and it does not match
+ * @d->saved_free.
+ */
+ freeable_cnt = c->freeable_cnt;
+ c->freeable_cnt = 0;
+ d->saved_free = ubifs_get_free_space_nolock(c);
+ c->freeable_cnt = freeable_cnt;
+ spin_unlock(&c->space_lock);
+}
+
+/**
+ * dbg_check_space_info - check flash space information.
+ * @c: UBIFS file-system description object
+ *
+ * This function compares current flash space information with the information
+ * which was saved when the 'dbg_save_space_info()' function was called.
+ * Returns zero if the information has not changed, and %-EINVAL it it has
+ * changed.
+ */
+int dbg_check_space_info(struct ubifs_info *c)
+{
+ struct ubifs_debug_info *d = c->dbg;
+ struct ubifs_lp_stats lst;
+ long long free;
+ int freeable_cnt;
+
+ spin_lock(&c->space_lock);
+ freeable_cnt = c->freeable_cnt;
+ c->freeable_cnt = 0;
+ free = ubifs_get_free_space_nolock(c);
+ c->freeable_cnt = freeable_cnt;
+ spin_unlock(&c->space_lock);
+
+ if (free != d->saved_free) {
+ ubifs_err(c, "free space changed from %lld to %lld",
+ d->saved_free, free);
+ goto out;
+ }
+
+ return 0;
+
+out:
+ ubifs_msg(c, "saved lprops statistics dump");
+ ubifs_dump_lstats(&d->saved_lst);
+ ubifs_msg(c, "saved budgeting info dump");
+ ubifs_dump_budg(c, &d->saved_bi);
+ ubifs_msg(c, "saved idx_gc_cnt %d", d->saved_idx_gc_cnt);
+ ubifs_msg(c, "current lprops statistics dump");
+ ubifs_get_lp_stats(c, &lst);
+ ubifs_dump_lstats(&lst);
+ ubifs_msg(c, "current budgeting info dump");
+ ubifs_dump_budg(c, &c->bi);
+ dump_stack();
+ return -EINVAL;
+}
+
+/**
+ * dbg_check_synced_i_size - check synchronized inode size.
+ * @c: UBIFS file-system description object
+ * @inode: inode to check
+ *
+ * If inode is clean, synchronized inode size has to be equivalent to current
+ * inode size. This function has to be called only for locked inodes (@i_mutex
+ * has to be locked). Returns %0 if synchronized inode size if correct, and
+ * %-EINVAL if not.
+ */
+int dbg_check_synced_i_size(const struct ubifs_info *c, struct inode *inode)
+{
+ int err = 0;
+ struct ubifs_inode *ui = ubifs_inode(inode);
+
+ if (!dbg_is_chk_gen(c))
+ return 0;
+ if (!S_ISREG(inode->i_mode))
+ return 0;
+
+ mutex_lock(&ui->ui_mutex);
+ spin_lock(&ui->ui_lock);
+ if (ui->ui_size != ui->synced_i_size && !ui->dirty) {
+ ubifs_err(c, "ui_size is %lld, synced_i_size is %lld, but inode is clean",
+ ui->ui_size, ui->synced_i_size);
+ ubifs_err(c, "i_ino %lu, i_mode %#x, i_size %lld", inode->i_ino,
+ inode->i_mode, i_size_read(inode));
+ dump_stack();
+ err = -EINVAL;
+ }
+ spin_unlock(&ui->ui_lock);
+ mutex_unlock(&ui->ui_mutex);
+ return err;
+}
+
+/*
+ * dbg_check_dir - check directory inode size and link count.
+ * @c: UBIFS file-system description object
+ * @dir: the directory to calculate size for
+ * @size: the result is returned here
+ *
+ * This function makes sure that directory size and link count are correct.
+ * Returns zero in case of success and a negative error code in case of
+ * failure.
+ *
+ * Note, it is good idea to make sure the @dir->i_mutex is locked before
+ * calling this function.
+ */
+int dbg_check_dir(struct ubifs_info *c, const struct inode *dir)
+{
+ unsigned int nlink = 2;
+ union ubifs_key key;
+ struct ubifs_dent_node *dent, *pdent = NULL;
+ struct qstr nm = { .name = NULL };
+ loff_t size = UBIFS_INO_NODE_SZ;
+
+ if (!dbg_is_chk_gen(c))
+ return 0;
+
+ if (!S_ISDIR(dir->i_mode))
+ return 0;
+
+ lowest_dent_key(c, &key, dir->i_ino);
+ while (1) {
+ int err;
+
+ dent = ubifs_tnc_next_ent(c, &key, &nm);
+ if (IS_ERR(dent)) {
+ err = PTR_ERR(dent);
+ if (err == -ENOENT)
+ break;
+ return err;
+ }
+
+ nm.name = dent->name;
+ nm.len = le16_to_cpu(dent->nlen);
+ size += CALC_DENT_SIZE(nm.len);
+ if (dent->type == UBIFS_ITYPE_DIR)
+ nlink += 1;
+ kfree(pdent);
+ pdent = dent;
+ key_read(c, &dent->key, &key);
+ }
+ kfree(pdent);
+
+ if (i_size_read(dir) != size) {
+ ubifs_err(c, "directory inode %lu has size %llu, but calculated size is %llu",
+ dir->i_ino, (unsigned long long)i_size_read(dir),
+ (unsigned long long)size);
+ ubifs_dump_inode(c, dir);
+ dump_stack();
+ return -EINVAL;
+ }
+ if (dir->i_nlink != nlink) {
+ ubifs_err(c, "directory inode %lu has nlink %u, but calculated nlink is %u",
+ dir->i_ino, dir->i_nlink, nlink);
+ ubifs_dump_inode(c, dir);
+ dump_stack();
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * dbg_check_key_order - make sure that colliding keys are properly ordered.
+ * @c: UBIFS file-system description object
+ * @zbr1: first zbranch
+ * @zbr2: following zbranch
+ *
+ * In UBIFS indexing B-tree colliding keys has to be sorted in binary order of
+ * names of the direntries/xentries which are referred by the keys. This
+ * function reads direntries/xentries referred by @zbr1 and @zbr2 and makes
+ * sure the name of direntry/xentry referred by @zbr1 is less than
+ * direntry/xentry referred by @zbr2. Returns zero if this is true, %1 if not,
+ * and a negative error code in case of failure.
+ */
+static int dbg_check_key_order(struct ubifs_info *c, struct ubifs_zbranch *zbr1,
+ struct ubifs_zbranch *zbr2)
+{
+ int err, nlen1, nlen2, cmp;
+ struct ubifs_dent_node *dent1, *dent2;
+ union ubifs_key key;
+ char key_buf[DBG_KEY_BUF_LEN];
+
+ ubifs_assert(!keys_cmp(c, &zbr1->key, &zbr2->key));
+ dent1 = kmalloc(UBIFS_MAX_DENT_NODE_SZ, GFP_NOFS);
+ if (!dent1)
+ return -ENOMEM;
+ dent2 = kmalloc(UBIFS_MAX_DENT_NODE_SZ, GFP_NOFS);
+ if (!dent2) {
+ err = -ENOMEM;
+ goto out_free;
+ }
+
+ err = ubifs_tnc_read_node(c, zbr1, dent1);
+ if (err)
+ goto out_free;
+ err = ubifs_validate_entry(c, dent1);
+ if (err)
+ goto out_free;
+
+ err = ubifs_tnc_read_node(c, zbr2, dent2);
+ if (err)
+ goto out_free;
+ err = ubifs_validate_entry(c, dent2);
+ if (err)
+ goto out_free;
+
+ /* Make sure node keys are the same as in zbranch */
+ err = 1;
+ key_read(c, &dent1->key, &key);
+ if (keys_cmp(c, &zbr1->key, &key)) {
+ ubifs_err(c, "1st entry at %d:%d has key %s", zbr1->lnum,
+ zbr1->offs, dbg_snprintf_key(c, &key, key_buf,
+ DBG_KEY_BUF_LEN));
+ ubifs_err(c, "but it should have key %s according to tnc",
+ dbg_snprintf_key(c, &zbr1->key, key_buf,
+ DBG_KEY_BUF_LEN));
+ ubifs_dump_node(c, dent1);
+ goto out_free;
+ }
+
+ key_read(c, &dent2->key, &key);
+ if (keys_cmp(c, &zbr2->key, &key)) {
+ ubifs_err(c, "2nd entry at %d:%d has key %s", zbr1->lnum,
+ zbr1->offs, dbg_snprintf_key(c, &key, key_buf,
+ DBG_KEY_BUF_LEN));
+ ubifs_err(c, "but it should have key %s according to tnc",
+ dbg_snprintf_key(c, &zbr2->key, key_buf,
+ DBG_KEY_BUF_LEN));
+ ubifs_dump_node(c, dent2);
+ goto out_free;
+ }
+
+ nlen1 = le16_to_cpu(dent1->nlen);
+ nlen2 = le16_to_cpu(dent2->nlen);
+
+ cmp = memcmp(dent1->name, dent2->name, min_t(int, nlen1, nlen2));
+ if (cmp < 0 || (cmp == 0 && nlen1 < nlen2)) {
+ err = 0;
+ goto out_free;
+ }
+ if (cmp == 0 && nlen1 == nlen2)
+ ubifs_err(c, "2 xent/dent nodes with the same name");
+ else
+ ubifs_err(c, "bad order of colliding key %s",
+ dbg_snprintf_key(c, &key, key_buf, DBG_KEY_BUF_LEN));
+
+ ubifs_msg(c, "first node at %d:%d\n", zbr1->lnum, zbr1->offs);
+ ubifs_dump_node(c, dent1);
+ ubifs_msg(c, "second node at %d:%d\n", zbr2->lnum, zbr2->offs);
+ ubifs_dump_node(c, dent2);
+
+out_free:
+ kfree(dent2);
+ kfree(dent1);
+ return err;
+}
+
+/**
+ * dbg_check_znode - check if znode is all right.
+ * @c: UBIFS file-system description object
+ * @zbr: zbranch which points to this znode
+ *
+ * This function makes sure that znode referred to by @zbr is all right.
+ * Returns zero if it is, and %-EINVAL if it is not.
+ */
+static int dbg_check_znode(struct ubifs_info *c, struct ubifs_zbranch *zbr)
+{
+ struct ubifs_znode *znode = zbr->znode;
+ struct ubifs_znode *zp = znode->parent;
+ int n, err, cmp;
+
+ if (znode->child_cnt <= 0 || znode->child_cnt > c->fanout) {
+ err = 1;
+ goto out;
+ }
+ if (znode->level < 0) {
+ err = 2;
+ goto out;
+ }
+ if (znode->iip < 0 || znode->iip >= c->fanout) {
+ err = 3;
+ goto out;
+ }
+
+ if (zbr->len == 0)
+ /* Only dirty zbranch may have no on-flash nodes */
+ if (!ubifs_zn_dirty(znode)) {
+ err = 4;
+ goto out;
+ }
+
+ if (ubifs_zn_dirty(znode)) {
+ /*
+ * If znode is dirty, its parent has to be dirty as well. The
+ * order of the operation is important, so we have to have
+ * memory barriers.
+ */
+ smp_mb();
+ if (zp && !ubifs_zn_dirty(zp)) {
+ /*
+ * The dirty flag is atomic and is cleared outside the
+ * TNC mutex, so znode's dirty flag may now have
+ * been cleared. The child is always cleared before the
+ * parent, so we just need to check again.
+ */
+ smp_mb();
+ if (ubifs_zn_dirty(znode)) {
+ err = 5;
+ goto out;
+ }
+ }
+ }
+
+ if (zp) {
+ const union ubifs_key *min, *max;
+
+ if (znode->level != zp->level - 1) {
+ err = 6;
+ goto out;
+ }
+
+ /* Make sure the 'parent' pointer in our znode is correct */
+ err = ubifs_search_zbranch(c, zp, &zbr->key, &n);
+ if (!err) {
+ /* This zbranch does not exist in the parent */
+ err = 7;
+ goto out;
+ }
+
+ if (znode->iip >= zp->child_cnt) {
+ err = 8;
+ goto out;
+ }
+
+ if (znode->iip != n) {
+ /* This may happen only in case of collisions */
+ if (keys_cmp(c, &zp->zbranch[n].key,
+ &zp->zbranch[znode->iip].key)) {
+ err = 9;
+ goto out;
+ }
+ n = znode->iip;
+ }
+
+ /*
+ * Make sure that the first key in our znode is greater than or
+ * equal to the key in the pointing zbranch.
+ */
+ min = &zbr->key;
+ cmp = keys_cmp(c, min, &znode->zbranch[0].key);
+ if (cmp == 1) {
+ err = 10;
+ goto out;
+ }
+
+ if (n + 1 < zp->child_cnt) {
+ max = &zp->zbranch[n + 1].key;
+
+ /*
+ * Make sure the last key in our znode is less or
+ * equivalent than the key in the zbranch which goes
+ * after our pointing zbranch.
+ */
+ cmp = keys_cmp(c, max,
+ &znode->zbranch[znode->child_cnt - 1].key);
+ if (cmp == -1) {
+ err = 11;
+ goto out;
+ }
+ }
+ } else {
+ /* This may only be root znode */
+ if (zbr != &c->zroot) {
+ err = 12;
+ goto out;
+ }
+ }
+
+ /*
+ * Make sure that next key is greater or equivalent then the previous
+ * one.
+ */
+ for (n = 1; n < znode->child_cnt; n++) {
+ cmp = keys_cmp(c, &znode->zbranch[n - 1].key,
+ &znode->zbranch[n].key);
+ if (cmp > 0) {
+ err = 13;
+ goto out;
+ }
+ if (cmp == 0) {
+ /* This can only be keys with colliding hash */
+ if (!is_hash_key(c, &znode->zbranch[n].key)) {
+ err = 14;
+ goto out;
+ }
+
+ if (znode->level != 0 || c->replaying)
+ continue;
+
+ /*
+ * Colliding keys should follow binary order of
+ * corresponding xentry/dentry names.
+ */
+ err = dbg_check_key_order(c, &znode->zbranch[n - 1],
+ &znode->zbranch[n]);
+ if (err < 0)
+ return err;
+ if (err) {
+ err = 15;
+ goto out;
+ }
+ }
+ }
+
+ for (n = 0; n < znode->child_cnt; n++) {
+ if (!znode->zbranch[n].znode &&
+ (znode->zbranch[n].lnum == 0 ||
+ znode->zbranch[n].len == 0)) {
+ err = 16;
+ goto out;
+ }
+
+ if (znode->zbranch[n].lnum != 0 &&
+ znode->zbranch[n].len == 0) {
+ err = 17;
+ goto out;
+ }
+
+ if (znode->zbranch[n].lnum == 0 &&
+ znode->zbranch[n].len != 0) {
+ err = 18;
+ goto out;
+ }
+
+ if (znode->zbranch[n].lnum == 0 &&
+ znode->zbranch[n].offs != 0) {
+ err = 19;
+ goto out;
+ }
+
+ if (znode->level != 0 && znode->zbranch[n].znode)
+ if (znode->zbranch[n].znode->parent != znode) {
+ err = 20;
+ goto out;
+ }
+ }
+
+ return 0;
+
+out:
+ ubifs_err(c, "failed, error %d", err);
+ ubifs_msg(c, "dump of the znode");
+ ubifs_dump_znode(c, znode);
+ if (zp) {
+ ubifs_msg(c, "dump of the parent znode");
+ ubifs_dump_znode(c, zp);
+ }
+ dump_stack();
+ return -EINVAL;
+}
+#else
+
+int dbg_check_dir(struct ubifs_info *c, const struct inode *dir)
+{
+ return 0;
+}
+
+void dbg_debugfs_exit_fs(struct ubifs_info *c)
+{
+ return;
+}
+
+int ubifs_debugging_init(struct ubifs_info *c)
+{
+ return 0;
+}
+void ubifs_debugging_exit(struct ubifs_info *c)
+{
+}
+int dbg_check_filesystem(struct ubifs_info *c)
+{
+ return 0;
+}
+int dbg_debugfs_init_fs(struct ubifs_info *c)
+{
+ return 0;
+}
+#endif
+
+#ifndef __UBOOT__
+/**
+ * dbg_check_tnc - check TNC tree.
+ * @c: UBIFS file-system description object
+ * @extra: do extra checks that are possible at start commit
+ *
+ * This function traverses whole TNC tree and checks every znode. Returns zero
+ * if everything is all right and %-EINVAL if something is wrong with TNC.
+ */
+int dbg_check_tnc(struct ubifs_info *c, int extra)
+{
+ struct ubifs_znode *znode;
+ long clean_cnt = 0, dirty_cnt = 0;
+ int err, last;
+
+ if (!dbg_is_chk_index(c))
+ return 0;
+
+ ubifs_assert(mutex_is_locked(&c->tnc_mutex));
+ if (!c->zroot.znode)
+ return 0;
+
+ znode = ubifs_tnc_postorder_first(c->zroot.znode);
+ while (1) {
+ struct ubifs_znode *prev;
+ struct ubifs_zbranch *zbr;
+
+ if (!znode->parent)
+ zbr = &c->zroot;
+ else
+ zbr = &znode->parent->zbranch[znode->iip];
+
+ err = dbg_check_znode(c, zbr);
+ if (err)
+ return err;
+
+ if (extra) {
+ if (ubifs_zn_dirty(znode))
+ dirty_cnt += 1;
+ else
+ clean_cnt += 1;
+ }
+
+ prev = znode;
+ znode = ubifs_tnc_postorder_next(znode);
+ if (!znode)
+ break;
+
+ /*
+ * If the last key of this znode is equivalent to the first key
+ * of the next znode (collision), then check order of the keys.
+ */
+ last = prev->child_cnt - 1;
+ if (prev->level == 0 && znode->level == 0 && !c->replaying &&
+ !keys_cmp(c, &prev->zbranch[last].key,
+ &znode->zbranch[0].key)) {
+ err = dbg_check_key_order(c, &prev->zbranch[last],
+ &znode->zbranch[0]);
+ if (err < 0)
+ return err;
+ if (err) {
+ ubifs_msg(c, "first znode");
+ ubifs_dump_znode(c, prev);
+ ubifs_msg(c, "second znode");
+ ubifs_dump_znode(c, znode);
+ return -EINVAL;
+ }
+ }
+ }
+
+ if (extra) {
+ if (clean_cnt != atomic_long_read(&c->clean_zn_cnt)) {
+ ubifs_err(c, "incorrect clean_zn_cnt %ld, calculated %ld",
+ atomic_long_read(&c->clean_zn_cnt),
+ clean_cnt);
+ return -EINVAL;
+ }
+ if (dirty_cnt != atomic_long_read(&c->dirty_zn_cnt)) {
+ ubifs_err(c, "incorrect dirty_zn_cnt %ld, calculated %ld",
+ atomic_long_read(&c->dirty_zn_cnt),
+ dirty_cnt);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+#else
+int dbg_check_tnc(struct ubifs_info *c, int extra)
+{
+ return 0;
+}
+#endif
+
+/**
+ * dbg_walk_index - walk the on-flash index.
+ * @c: UBIFS file-system description object
+ * @leaf_cb: called for each leaf node
+ * @znode_cb: called for each indexing node
+ * @priv: private data which is passed to callbacks
+ *
+ * This function walks the UBIFS index and calls the @leaf_cb for each leaf
+ * node and @znode_cb for each indexing node. Returns zero in case of success
+ * and a negative error code in case of failure.
+ *
+ * It would be better if this function removed every znode it pulled to into
+ * the TNC, so that the behavior more closely matched the non-debugging
+ * behavior.
+ */
+int dbg_walk_index(struct ubifs_info *c, dbg_leaf_callback leaf_cb,
+ dbg_znode_callback znode_cb, void *priv)
+{
+ int err;
+ struct ubifs_zbranch *zbr;
+ struct ubifs_znode *znode, *child;
+
+ mutex_lock(&c->tnc_mutex);
+ /* If the root indexing node is not in TNC - pull it */
+ if (!c->zroot.znode) {
+ c->zroot.znode = ubifs_load_znode(c, &c->zroot, NULL, 0);
+ if (IS_ERR(c->zroot.znode)) {
+ err = PTR_ERR(c->zroot.znode);
+ c->zroot.znode = NULL;
+ goto out_unlock;
+ }
+ }
+
+ /*
+ * We are going to traverse the indexing tree in the postorder manner.
+ * Go down and find the leftmost indexing node where we are going to
+ * start from.
+ */
+ znode = c->zroot.znode;
+ while (znode->level > 0) {
+ zbr = &znode->zbranch[0];
+ child = zbr->znode;
+ if (!child) {
+ child = ubifs_load_znode(c, zbr, znode, 0);
+ if (IS_ERR(child)) {
+ err = PTR_ERR(child);
+ goto out_unlock;
+ }
+ zbr->znode = child;
+ }
+
+ znode = child;
+ }
+
+ /* Iterate over all indexing nodes */
+ while (1) {
+ int idx;
+
+ cond_resched();
+
+ if (znode_cb) {
+ err = znode_cb(c, znode, priv);
+ if (err) {
+ ubifs_err(c, "znode checking function returned error %d",
+ err);
+ ubifs_dump_znode(c, znode);
+ goto out_dump;
+ }
+ }
+ if (leaf_cb && znode->level == 0) {
+ for (idx = 0; idx < znode->child_cnt; idx++) {
+ zbr = &znode->zbranch[idx];
+ err = leaf_cb(c, zbr, priv);
+ if (err) {
+ ubifs_err(c, "leaf checking function returned error %d, for leaf at LEB %d:%d",
+ err, zbr->lnum, zbr->offs);
+ goto out_dump;
+ }
+ }
+ }
+
+ if (!znode->parent)
+ break;
+
+ idx = znode->iip + 1;
+ znode = znode->parent;
+ if (idx < znode->child_cnt) {
+ /* Switch to the next index in the parent */
+ zbr = &znode->zbranch[idx];
+ child = zbr->znode;
+ if (!child) {
+ child = ubifs_load_znode(c, zbr, znode, idx);
+ if (IS_ERR(child)) {
+ err = PTR_ERR(child);
+ goto out_unlock;
+ }
+ zbr->znode = child;
+ }
+ znode = child;
+ } else
+ /*
+ * This is the last child, switch to the parent and
+ * continue.
+ */
+ continue;
+
+ /* Go to the lowest leftmost znode in the new sub-tree */
+ while (znode->level > 0) {
+ zbr = &znode->zbranch[0];
+ child = zbr->znode;
+ if (!child) {
+ child = ubifs_load_znode(c, zbr, znode, 0);
+ if (IS_ERR(child)) {
+ err = PTR_ERR(child);
+ goto out_unlock;
+ }
+ zbr->znode = child;
+ }
+ znode = child;
+ }
+ }
+
+ mutex_unlock(&c->tnc_mutex);
+ return 0;
+
+out_dump:
+ if (znode->parent)
+ zbr = &znode->parent->zbranch[znode->iip];
+ else
+ zbr = &c->zroot;
+ ubifs_msg(c, "dump of znode at LEB %d:%d", zbr->lnum, zbr->offs);
+ ubifs_dump_znode(c, znode);
+out_unlock:
+ mutex_unlock(&c->tnc_mutex);
+ return err;
+}
+
+/**
+ * add_size - add znode size to partially calculated index size.
+ * @c: UBIFS file-system description object
+ * @znode: znode to add size for
+ * @priv: partially calculated index size
+ *
+ * This is a helper function for 'dbg_check_idx_size()' which is called for
+ * every indexing node and adds its size to the 'long long' variable pointed to
+ * by @priv.
+ */
+static int add_size(struct ubifs_info *c, struct ubifs_znode *znode, void *priv)
+{
+ long long *idx_size = priv;
+ int add;
+
+ add = ubifs_idx_node_sz(c, znode->child_cnt);
+ add = ALIGN(add, 8);
+ *idx_size += add;
+ return 0;
+}
+
+/**
+ * dbg_check_idx_size - check index size.
+ * @c: UBIFS file-system description object
+ * @idx_size: size to check
+ *
+ * This function walks the UBIFS index, calculates its size and checks that the
+ * size is equivalent to @idx_size. Returns zero in case of success and a
+ * negative error code in case of failure.
+ */
+int dbg_check_idx_size(struct ubifs_info *c, long long idx_size)
+{
+ int err;
+ long long calc = 0;
+
+ if (!dbg_is_chk_index(c))
+ return 0;
+
+ err = dbg_walk_index(c, NULL, add_size, &calc);
+ if (err) {
+ ubifs_err(c, "error %d while walking the index", err);
+ return err;
+ }
+
+ if (calc != idx_size) {
+ ubifs_err(c, "index size check failed: calculated size is %lld, should be %lld",
+ calc, idx_size);
+ dump_stack();
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#ifndef __UBOOT__
+/**
+ * struct fsck_inode - information about an inode used when checking the file-system.
+ * @rb: link in the RB-tree of inodes
+ * @inum: inode number
+ * @mode: inode type, permissions, etc
+ * @nlink: inode link count
+ * @xattr_cnt: count of extended attributes
+ * @references: how many directory/xattr entries refer this inode (calculated
+ * while walking the index)
+ * @calc_cnt: for directory inode count of child directories
+ * @size: inode size (read from on-flash inode)
+ * @xattr_sz: summary size of all extended attributes (read from on-flash
+ * inode)
+ * @calc_sz: for directories calculated directory size
+ * @calc_xcnt: count of extended attributes
+ * @calc_xsz: calculated summary size of all extended attributes
+ * @xattr_nms: sum of lengths of all extended attribute names belonging to this
+ * inode (read from on-flash inode)
+ * @calc_xnms: calculated sum of lengths of all extended attribute names
+ */
+struct fsck_inode {
+ struct rb_node rb;
+ ino_t inum;
+ umode_t mode;
+ unsigned int nlink;
+ unsigned int xattr_cnt;
+ int references;
+ int calc_cnt;
+ long long size;
+ unsigned int xattr_sz;
+ long long calc_sz;
+ long long calc_xcnt;
+ long long calc_xsz;
+ unsigned int xattr_nms;
+ long long calc_xnms;
+};
+
+/**
+ * struct fsck_data - private FS checking information.
+ * @inodes: RB-tree of all inodes (contains @struct fsck_inode objects)
+ */
+struct fsck_data {
+ struct rb_root inodes;
+};
+
+/**
+ * add_inode - add inode information to RB-tree of inodes.
+ * @c: UBIFS file-system description object
+ * @fsckd: FS checking information
+ * @ino: raw UBIFS inode to add
+ *
+ * This is a helper function for 'check_leaf()' which adds information about
+ * inode @ino to the RB-tree of inodes. Returns inode information pointer in
+ * case of success and a negative error code in case of failure.
+ */
+static struct fsck_inode *add_inode(struct ubifs_info *c,
+ struct fsck_data *fsckd,
+ struct ubifs_ino_node *ino)
+{
+ struct rb_node **p, *parent = NULL;
+ struct fsck_inode *fscki;
+ ino_t inum = key_inum_flash(c, &ino->key);
+ struct inode *inode;
+ struct ubifs_inode *ui;
+
+ p = &fsckd->inodes.rb_node;
+ while (*p) {
+ parent = *p;
+ fscki = rb_entry(parent, struct fsck_inode, rb);
+ if (inum < fscki->inum)
+ p = &(*p)->rb_left;
+ else if (inum > fscki->inum)
+ p = &(*p)->rb_right;
+ else
+ return fscki;
+ }
+
+ if (inum > c->highest_inum) {
+ ubifs_err(c, "too high inode number, max. is %lu",
+ (unsigned long)c->highest_inum);
+ return ERR_PTR(-EINVAL);
+ }
+
+ fscki = kzalloc(sizeof(struct fsck_inode), GFP_NOFS);
+ if (!fscki)
+ return ERR_PTR(-ENOMEM);
+
+ inode = ilookup(c->vfs_sb, inum);
+
+ fscki->inum = inum;
+ /*
+ * If the inode is present in the VFS inode cache, use it instead of
+ * the on-flash inode which might be out-of-date. E.g., the size might
+ * be out-of-date. If we do not do this, the following may happen, for
+ * example:
+ * 1. A power cut happens
+ * 2. We mount the file-system R/O, the replay process fixes up the
+ * inode size in the VFS cache, but on on-flash.
+ * 3. 'check_leaf()' fails because it hits a data node beyond inode
+ * size.
+ */
+ if (!inode) {
+ fscki->nlink = le32_to_cpu(ino->nlink);
+ fscki->size = le64_to_cpu(ino->size);
+ fscki->xattr_cnt = le32_to_cpu(ino->xattr_cnt);
+ fscki->xattr_sz = le32_to_cpu(ino->xattr_size);
+ fscki->xattr_nms = le32_to_cpu(ino->xattr_names);
+ fscki->mode = le32_to_cpu(ino->mode);
+ } else {
+ ui = ubifs_inode(inode);
+ fscki->nlink = inode->i_nlink;
+ fscki->size = inode->i_size;
+ fscki->xattr_cnt = ui->xattr_cnt;
+ fscki->xattr_sz = ui->xattr_size;
+ fscki->xattr_nms = ui->xattr_names;
+ fscki->mode = inode->i_mode;
+ iput(inode);
+ }
+
+ if (S_ISDIR(fscki->mode)) {
+ fscki->calc_sz = UBIFS_INO_NODE_SZ;
+ fscki->calc_cnt = 2;
+ }
+
+ rb_link_node(&fscki->rb, parent, p);
+ rb_insert_color(&fscki->rb, &fsckd->inodes);
+
+ return fscki;
+}
+
+/**
+ * search_inode - search inode in the RB-tree of inodes.
+ * @fsckd: FS checking information
+ * @inum: inode number to search
+ *
+ * This is a helper function for 'check_leaf()' which searches inode @inum in
+ * the RB-tree of inodes and returns an inode information pointer or %NULL if
+ * the inode was not found.
+ */
+static struct fsck_inode *search_inode(struct fsck_data *fsckd, ino_t inum)
+{
+ struct rb_node *p;
+ struct fsck_inode *fscki;
+
+ p = fsckd->inodes.rb_node;
+ while (p) {
+ fscki = rb_entry(p, struct fsck_inode, rb);
+ if (inum < fscki->inum)
+ p = p->rb_left;
+ else if (inum > fscki->inum)
+ p = p->rb_right;
+ else
+ return fscki;
+ }
+ return NULL;
+}
+
+/**
+ * read_add_inode - read inode node and add it to RB-tree of inodes.
+ * @c: UBIFS file-system description object
+ * @fsckd: FS checking information
+ * @inum: inode number to read
+ *
+ * This is a helper function for 'check_leaf()' which finds inode node @inum in
+ * the index, reads it, and adds it to the RB-tree of inodes. Returns inode
+ * information pointer in case of success and a negative error code in case of
+ * failure.
+ */
+static struct fsck_inode *read_add_inode(struct ubifs_info *c,
+ struct fsck_data *fsckd, ino_t inum)
+{
+ int n, err;
+ union ubifs_key key;
+ struct ubifs_znode *znode;
+ struct ubifs_zbranch *zbr;
+ struct ubifs_ino_node *ino;
+ struct fsck_inode *fscki;
+
+ fscki = search_inode(fsckd, inum);
+ if (fscki)
+ return fscki;
+
+ ino_key_init(c, &key, inum);
+ err = ubifs_lookup_level0(c, &key, &znode, &n);
+ if (!err) {
+ ubifs_err(c, "inode %lu not found in index", (unsigned long)inum);
+ return ERR_PTR(-ENOENT);
+ } else if (err < 0) {
+ ubifs_err(c, "error %d while looking up inode %lu",
+ err, (unsigned long)inum);
+ return ERR_PTR(err);
+ }
+
+ zbr = &znode->zbranch[n];
+ if (zbr->len < UBIFS_INO_NODE_SZ) {
+ ubifs_err(c, "bad node %lu node length %d",
+ (unsigned long)inum, zbr->len);
+ return ERR_PTR(-EINVAL);
+ }
+
+ ino = kmalloc(zbr->len, GFP_NOFS);
+ if (!ino)
+ return ERR_PTR(-ENOMEM);
+
+ err = ubifs_tnc_read_node(c, zbr, ino);
+ if (err) {
+ ubifs_err(c, "cannot read inode node at LEB %d:%d, error %d",
+ zbr->lnum, zbr->offs, err);
+ kfree(ino);
+ return ERR_PTR(err);
+ }
+
+ fscki = add_inode(c, fsckd, ino);
+ kfree(ino);
+ if (IS_ERR(fscki)) {
+ ubifs_err(c, "error %ld while adding inode %lu node",
+ PTR_ERR(fscki), (unsigned long)inum);
+ return fscki;
+ }
+
+ return fscki;
+}
+
+/**
+ * check_leaf - check leaf node.
+ * @c: UBIFS file-system description object
+ * @zbr: zbranch of the leaf node to check
+ * @priv: FS checking information
+ *
+ * This is a helper function for 'dbg_check_filesystem()' which is called for
+ * every single leaf node while walking the indexing tree. It checks that the
+ * leaf node referred from the indexing tree exists, has correct CRC, and does
+ * some other basic validation. This function is also responsible for building
+ * an RB-tree of inodes - it adds all inodes into the RB-tree. It also
+ * calculates reference count, size, etc for each inode in order to later
+ * compare them to the information stored inside the inodes and detect possible
+ * inconsistencies. Returns zero in case of success and a negative error code
+ * in case of failure.
+ */
+static int check_leaf(struct ubifs_info *c, struct ubifs_zbranch *zbr,
+ void *priv)
+{
+ ino_t inum;
+ void *node;
+ struct ubifs_ch *ch;
+ int err, type = key_type(c, &zbr->key);
+ struct fsck_inode *fscki;
+
+ if (zbr->len < UBIFS_CH_SZ) {
+ ubifs_err(c, "bad leaf length %d (LEB %d:%d)",
+ zbr->len, zbr->lnum, zbr->offs);
+ return -EINVAL;
+ }
+
+ node = kmalloc(zbr->len, GFP_NOFS);
+ if (!node)
+ return -ENOMEM;
+
+ err = ubifs_tnc_read_node(c, zbr, node);
+ if (err) {
+ ubifs_err(c, "cannot read leaf node at LEB %d:%d, error %d",
+ zbr->lnum, zbr->offs, err);
+ goto out_free;
+ }
+
+ /* If this is an inode node, add it to RB-tree of inodes */
+ if (type == UBIFS_INO_KEY) {
+ fscki = add_inode(c, priv, node);
+ if (IS_ERR(fscki)) {
+ err = PTR_ERR(fscki);
+ ubifs_err(c, "error %d while adding inode node", err);
+ goto out_dump;
+ }
+ goto out;
+ }
+
+ if (type != UBIFS_DENT_KEY && type != UBIFS_XENT_KEY &&
+ type != UBIFS_DATA_KEY) {
+ ubifs_err(c, "unexpected node type %d at LEB %d:%d",
+ type, zbr->lnum, zbr->offs);
+ err = -EINVAL;
+ goto out_free;
+ }
+
+ ch = node;
+ if (le64_to_cpu(ch->sqnum) > c->max_sqnum) {
+ ubifs_err(c, "too high sequence number, max. is %llu",
+ c->max_sqnum);
+ err = -EINVAL;
+ goto out_dump;
+ }
+
+ if (type == UBIFS_DATA_KEY) {
+ long long blk_offs;
+ struct ubifs_data_node *dn = node;
+
+ ubifs_assert(zbr->len >= UBIFS_DATA_NODE_SZ);
+
+ /*
+ * Search the inode node this data node belongs to and insert
+ * it to the RB-tree of inodes.
+ */
+ inum = key_inum_flash(c, &dn->key);
+ fscki = read_add_inode(c, priv, inum);
+ if (IS_ERR(fscki)) {
+ err = PTR_ERR(fscki);
+ ubifs_err(c, "error %d while processing data node and trying to find inode node %lu",
+ err, (unsigned long)inum);
+ goto out_dump;
+ }
+
+ /* Make sure the data node is within inode size */
+ blk_offs = key_block_flash(c, &dn->key);
+ blk_offs <<= UBIFS_BLOCK_SHIFT;
+ blk_offs += le32_to_cpu(dn->size);
+ if (blk_offs > fscki->size) {
+ ubifs_err(c, "data node at LEB %d:%d is not within inode size %lld",
+ zbr->lnum, zbr->offs, fscki->size);
+ err = -EINVAL;
+ goto out_dump;
+ }
+ } else {
+ int nlen;
+ struct ubifs_dent_node *dent = node;
+ struct fsck_inode *fscki1;
+
+ ubifs_assert(zbr->len >= UBIFS_DENT_NODE_SZ);
+
+ err = ubifs_validate_entry(c, dent);
+ if (err)
+ goto out_dump;
+
+ /*
+ * Search the inode node this entry refers to and the parent
+ * inode node and insert them to the RB-tree of inodes.
+ */
+ inum = le64_to_cpu(dent->inum);
+ fscki = read_add_inode(c, priv, inum);
+ if (IS_ERR(fscki)) {
+ err = PTR_ERR(fscki);
+ ubifs_err(c, "error %d while processing entry node and trying to find inode node %lu",
+ err, (unsigned long)inum);
+ goto out_dump;
+ }
+
+ /* Count how many direntries or xentries refers this inode */
+ fscki->references += 1;
+
+ inum = key_inum_flash(c, &dent->key);
+ fscki1 = read_add_inode(c, priv, inum);
+ if (IS_ERR(fscki1)) {
+ err = PTR_ERR(fscki1);
+ ubifs_err(c, "error %d while processing entry node and trying to find parent inode node %lu",
+ err, (unsigned long)inum);
+ goto out_dump;
+ }
+
+ nlen = le16_to_cpu(dent->nlen);
+ if (type == UBIFS_XENT_KEY) {
+ fscki1->calc_xcnt += 1;
+ fscki1->calc_xsz += CALC_DENT_SIZE(nlen);
+ fscki1->calc_xsz += CALC_XATTR_BYTES(fscki->size);
+ fscki1->calc_xnms += nlen;
+ } else {
+ fscki1->calc_sz += CALC_DENT_SIZE(nlen);
+ if (dent->type == UBIFS_ITYPE_DIR)
+ fscki1->calc_cnt += 1;
+ }
+ }
+
+out:
+ kfree(node);
+ return 0;
+
+out_dump:
+ ubifs_msg(c, "dump of node at LEB %d:%d", zbr->lnum, zbr->offs);
+ ubifs_dump_node(c, node);
+out_free:
+ kfree(node);
+ return err;
+}
+
+/**
+ * free_inodes - free RB-tree of inodes.
+ * @fsckd: FS checking information
+ */
+static void free_inodes(struct fsck_data *fsckd)
+{
+ struct fsck_inode *fscki, *n;
+
+ rbtree_postorder_for_each_entry_safe(fscki, n, &fsckd->inodes, rb)
+ kfree(fscki);
+}
+
+/**
+ * check_inodes - checks all inodes.
+ * @c: UBIFS file-system description object
+ * @fsckd: FS checking information
+ *
+ * This is a helper function for 'dbg_check_filesystem()' which walks the
+ * RB-tree of inodes after the index scan has been finished, and checks that
+ * inode nlink, size, etc are correct. Returns zero if inodes are fine,
+ * %-EINVAL if not, and a negative error code in case of failure.
+ */
+static int check_inodes(struct ubifs_info *c, struct fsck_data *fsckd)
+{
+ int n, err;
+ union ubifs_key key;
+ struct ubifs_znode *znode;
+ struct ubifs_zbranch *zbr;
+ struct ubifs_ino_node *ino;
+ struct fsck_inode *fscki;
+ struct rb_node *this = rb_first(&fsckd->inodes);
+
+ while (this) {
+ fscki = rb_entry(this, struct fsck_inode, rb);
+ this = rb_next(this);
+
+ if (S_ISDIR(fscki->mode)) {
+ /*
+ * Directories have to have exactly one reference (they
+ * cannot have hardlinks), although root inode is an
+ * exception.
+ */
+ if (fscki->inum != UBIFS_ROOT_INO &&
+ fscki->references != 1) {
+ ubifs_err(c, "directory inode %lu has %d direntries which refer it, but should be 1",
+ (unsigned long)fscki->inum,
+ fscki->references);
+ goto out_dump;
+ }
+ if (fscki->inum == UBIFS_ROOT_INO &&
+ fscki->references != 0) {
+ ubifs_err(c, "root inode %lu has non-zero (%d) direntries which refer it",
+ (unsigned long)fscki->inum,
+ fscki->references);
+ goto out_dump;
+ }
+ if (fscki->calc_sz != fscki->size) {
+ ubifs_err(c, "directory inode %lu size is %lld, but calculated size is %lld",
+ (unsigned long)fscki->inum,
+ fscki->size, fscki->calc_sz);
+ goto out_dump;
+ }
+ if (fscki->calc_cnt != fscki->nlink) {
+ ubifs_err(c, "directory inode %lu nlink is %d, but calculated nlink is %d",
+ (unsigned long)fscki->inum,
+ fscki->nlink, fscki->calc_cnt);
+ goto out_dump;
+ }
+ } else {
+ if (fscki->references != fscki->nlink) {
+ ubifs_err(c, "inode %lu nlink is %d, but calculated nlink is %d",
+ (unsigned long)fscki->inum,
+ fscki->nlink, fscki->references);
+ goto out_dump;
+ }
+ }
+ if (fscki->xattr_sz != fscki->calc_xsz) {
+ ubifs_err(c, "inode %lu has xattr size %u, but calculated size is %lld",
+ (unsigned long)fscki->inum, fscki->xattr_sz,
+ fscki->calc_xsz);
+ goto out_dump;
+ }
+ if (fscki->xattr_cnt != fscki->calc_xcnt) {
+ ubifs_err(c, "inode %lu has %u xattrs, but calculated count is %lld",
+ (unsigned long)fscki->inum,
+ fscki->xattr_cnt, fscki->calc_xcnt);
+ goto out_dump;
+ }
+ if (fscki->xattr_nms != fscki->calc_xnms) {
+ ubifs_err(c, "inode %lu has xattr names' size %u, but calculated names' size is %lld",
+ (unsigned long)fscki->inum, fscki->xattr_nms,
+ fscki->calc_xnms);
+ goto out_dump;
+ }
+ }
+
+ return 0;
+
+out_dump:
+ /* Read the bad inode and dump it */
+ ino_key_init(c, &key, fscki->inum);
+ err = ubifs_lookup_level0(c, &key, &znode, &n);
+ if (!err) {
+ ubifs_err(c, "inode %lu not found in index",
+ (unsigned long)fscki->inum);
+ return -ENOENT;
+ } else if (err < 0) {
+ ubifs_err(c, "error %d while looking up inode %lu",
+ err, (unsigned long)fscki->inum);
+ return err;
+ }
+
+ zbr = &znode->zbranch[n];
+ ino = kmalloc(zbr->len, GFP_NOFS);
+ if (!ino)
+ return -ENOMEM;
+
+ err = ubifs_tnc_read_node(c, zbr, ino);
+ if (err) {
+ ubifs_err(c, "cannot read inode node at LEB %d:%d, error %d",
+ zbr->lnum, zbr->offs, err);
+ kfree(ino);
+ return err;
+ }
+
+ ubifs_msg(c, "dump of the inode %lu sitting in LEB %d:%d",
+ (unsigned long)fscki->inum, zbr->lnum, zbr->offs);
+ ubifs_dump_node(c, ino);
+ kfree(ino);
+ return -EINVAL;
+}
+
+/**
+ * dbg_check_filesystem - check the file-system.
+ * @c: UBIFS file-system description object
+ *
+ * This function checks the file system, namely:
+ * o makes sure that all leaf nodes exist and their CRCs are correct;
+ * o makes sure inode nlink, size, xattr size/count are correct (for all
+ * inodes).
+ *
+ * The function reads whole indexing tree and all nodes, so it is pretty
+ * heavy-weight. Returns zero if the file-system is consistent, %-EINVAL if
+ * not, and a negative error code in case of failure.
+ */
+int dbg_check_filesystem(struct ubifs_info *c)
+{
+ int err;
+ struct fsck_data fsckd;
+
+ if (!dbg_is_chk_fs(c))
+ return 0;
+
+ fsckd.inodes = RB_ROOT;
+ err = dbg_walk_index(c, check_leaf, NULL, &fsckd);
+ if (err)
+ goto out_free;
+
+ err = check_inodes(c, &fsckd);
+ if (err)
+ goto out_free;
+
+ free_inodes(&fsckd);
+ return 0;
+
+out_free:
+ ubifs_err(c, "file-system check failed with error %d", err);
+ dump_stack();
+ free_inodes(&fsckd);
+ return err;
+}
+
+/**
+ * dbg_check_data_nodes_order - check that list of data nodes is sorted.
+ * @c: UBIFS file-system description object
+ * @head: the list of nodes ('struct ubifs_scan_node' objects)
+ *
+ * This function returns zero if the list of data nodes is sorted correctly,
+ * and %-EINVAL if not.
+ */
+int dbg_check_data_nodes_order(struct ubifs_info *c, struct list_head *head)
+{
+ struct list_head *cur;
+ struct ubifs_scan_node *sa, *sb;
+
+ if (!dbg_is_chk_gen(c))
+ return 0;
+
+ for (cur = head->next; cur->next != head; cur = cur->next) {
+ ino_t inuma, inumb;
+ uint32_t blka, blkb;
+
+ cond_resched();
+ sa = container_of(cur, struct ubifs_scan_node, list);
+ sb = container_of(cur->next, struct ubifs_scan_node, list);
+
+ if (sa->type != UBIFS_DATA_NODE) {
+ ubifs_err(c, "bad node type %d", sa->type);
+ ubifs_dump_node(c, sa->node);
+ return -EINVAL;
+ }
+ if (sb->type != UBIFS_DATA_NODE) {
+ ubifs_err(c, "bad node type %d", sb->type);
+ ubifs_dump_node(c, sb->node);
+ return -EINVAL;
+ }
+
+ inuma = key_inum(c, &sa->key);
+ inumb = key_inum(c, &sb->key);
+
+ if (inuma < inumb)
+ continue;
+ if (inuma > inumb) {
+ ubifs_err(c, "larger inum %lu goes before inum %lu",
+ (unsigned long)inuma, (unsigned long)inumb);
+ goto error_dump;
+ }
+
+ blka = key_block(c, &sa->key);
+ blkb = key_block(c, &sb->key);
+
+ if (blka > blkb) {
+ ubifs_err(c, "larger block %u goes before %u", blka, blkb);
+ goto error_dump;
+ }
+ if (blka == blkb) {
+ ubifs_err(c, "two data nodes for the same block");
+ goto error_dump;
+ }
+ }
+
+ return 0;
+
+error_dump:
+ ubifs_dump_node(c, sa->node);
+ ubifs_dump_node(c, sb->node);
+ return -EINVAL;
+}
+
+/**
+ * dbg_check_nondata_nodes_order - check that list of data nodes is sorted.
+ * @c: UBIFS file-system description object
+ * @head: the list of nodes ('struct ubifs_scan_node' objects)
+ *
+ * This function returns zero if the list of non-data nodes is sorted correctly,
+ * and %-EINVAL if not.
+ */
+int dbg_check_nondata_nodes_order(struct ubifs_info *c, struct list_head *head)
+{
+ struct list_head *cur;
+ struct ubifs_scan_node *sa, *sb;
+
+ if (!dbg_is_chk_gen(c))
+ return 0;
+
+ for (cur = head->next; cur->next != head; cur = cur->next) {
+ ino_t inuma, inumb;
+ uint32_t hasha, hashb;
+
+ cond_resched();
+ sa = container_of(cur, struct ubifs_scan_node, list);
+ sb = container_of(cur->next, struct ubifs_scan_node, list);
+
+ if (sa->type != UBIFS_INO_NODE && sa->type != UBIFS_DENT_NODE &&
+ sa->type != UBIFS_XENT_NODE) {
+ ubifs_err(c, "bad node type %d", sa->type);
+ ubifs_dump_node(c, sa->node);
+ return -EINVAL;
+ }
+ if (sa->type != UBIFS_INO_NODE && sa->type != UBIFS_DENT_NODE &&
+ sa->type != UBIFS_XENT_NODE) {
+ ubifs_err(c, "bad node type %d", sb->type);
+ ubifs_dump_node(c, sb->node);
+ return -EINVAL;
+ }
+
+ if (sa->type != UBIFS_INO_NODE && sb->type == UBIFS_INO_NODE) {
+ ubifs_err(c, "non-inode node goes before inode node");
+ goto error_dump;
+ }
+
+ if (sa->type == UBIFS_INO_NODE && sb->type != UBIFS_INO_NODE)
+ continue;
+
+ if (sa->type == UBIFS_INO_NODE && sb->type == UBIFS_INO_NODE) {
+ /* Inode nodes are sorted in descending size order */
+ if (sa->len < sb->len) {
+ ubifs_err(c, "smaller inode node goes first");
+ goto error_dump;
+ }
+ continue;
+ }
+
+ /*
+ * This is either a dentry or xentry, which should be sorted in
+ * ascending (parent ino, hash) order.
+ */
+ inuma = key_inum(c, &sa->key);
+ inumb = key_inum(c, &sb->key);
+
+ if (inuma < inumb)
+ continue;
+ if (inuma > inumb) {
+ ubifs_err(c, "larger inum %lu goes before inum %lu",
+ (unsigned long)inuma, (unsigned long)inumb);
+ goto error_dump;
+ }
+
+ hasha = key_block(c, &sa->key);
+ hashb = key_block(c, &sb->key);
+
+ if (hasha > hashb) {
+ ubifs_err(c, "larger hash %u goes before %u",
+ hasha, hashb);
+ goto error_dump;
+ }
+ }
+
+ return 0;
+
+error_dump:
+ ubifs_msg(c, "dumping first node");
+ ubifs_dump_node(c, sa->node);
+ ubifs_msg(c, "dumping second node");
+ ubifs_dump_node(c, sb->node);
+ return -EINVAL;
+ return 0;
+}
+
+static inline int chance(unsigned int n, unsigned int out_of)
+{
+ return !!((prandom_u32() % out_of) + 1 <= n);
+
+}
+
+static int power_cut_emulated(struct ubifs_info *c, int lnum, int write)
+{
+ struct ubifs_debug_info *d = c->dbg;
+
+ ubifs_assert(dbg_is_tst_rcvry(c));
+
+ if (!d->pc_cnt) {
+ /* First call - decide delay to the power cut */
+ if (chance(1, 2)) {
+ unsigned long delay;
+
+ if (chance(1, 2)) {
+ d->pc_delay = 1;
+ /* Fail within 1 minute */
+ delay = prandom_u32() % 60000;
+ d->pc_timeout = jiffies;
+ d->pc_timeout += msecs_to_jiffies(delay);
+ ubifs_warn(c, "failing after %lums", delay);
+ } else {
+ d->pc_delay = 2;
+ delay = prandom_u32() % 10000;
+ /* Fail within 10000 operations */
+ d->pc_cnt_max = delay;
+ ubifs_warn(c, "failing after %lu calls", delay);
+ }
+ }
+
+ d->pc_cnt += 1;
+ }
+
+ /* Determine if failure delay has expired */
+ if (d->pc_delay == 1 && time_before(jiffies, d->pc_timeout))
+ return 0;
+ if (d->pc_delay == 2 && d->pc_cnt++ < d->pc_cnt_max)
+ return 0;
+
+ if (lnum == UBIFS_SB_LNUM) {
+ if (write && chance(1, 2))
+ return 0;
+ if (chance(19, 20))
+ return 0;
+ ubifs_warn(c, "failing in super block LEB %d", lnum);
+ } else if (lnum == UBIFS_MST_LNUM || lnum == UBIFS_MST_LNUM + 1) {
+ if (chance(19, 20))
+ return 0;
+ ubifs_warn(c, "failing in master LEB %d", lnum);
+ } else if (lnum >= UBIFS_LOG_LNUM && lnum <= c->log_last) {
+ if (write && chance(99, 100))
+ return 0;
+ if (chance(399, 400))
+ return 0;
+ ubifs_warn(c, "failing in log LEB %d", lnum);
+ } else if (lnum >= c->lpt_first && lnum <= c->lpt_last) {
+ if (write && chance(7, 8))
+ return 0;
+ if (chance(19, 20))
+ return 0;
+ ubifs_warn(c, "failing in LPT LEB %d", lnum);
+ } else if (lnum >= c->orph_first && lnum <= c->orph_last) {
+ if (write && chance(1, 2))
+ return 0;
+ if (chance(9, 10))
+ return 0;
+ ubifs_warn(c, "failing in orphan LEB %d", lnum);
+ } else if (lnum == c->ihead_lnum) {
+ if (chance(99, 100))
+ return 0;
+ ubifs_warn(c, "failing in index head LEB %d", lnum);
+ } else if (c->jheads && lnum == c->jheads[GCHD].wbuf.lnum) {
+ if (chance(9, 10))
+ return 0;
+ ubifs_warn(c, "failing in GC head LEB %d", lnum);
+ } else if (write && !RB_EMPTY_ROOT(&c->buds) &&
+ !ubifs_search_bud(c, lnum)) {
+ if (chance(19, 20))
+ return 0;
+ ubifs_warn(c, "failing in non-bud LEB %d", lnum);
+ } else if (c->cmt_state == COMMIT_RUNNING_BACKGROUND ||
+ c->cmt_state == COMMIT_RUNNING_REQUIRED) {
+ if (chance(999, 1000))
+ return 0;
+ ubifs_warn(c, "failing in bud LEB %d commit running", lnum);
+ } else {
+ if (chance(9999, 10000))
+ return 0;
+ ubifs_warn(c, "failing in bud LEB %d commit not running", lnum);
+ }
+
+ d->pc_happened = 1;
+ ubifs_warn(c, "========== Power cut emulated ==========");
+ dump_stack();
+ return 1;
+}
+
+static int corrupt_data(const struct ubifs_info *c, const void *buf,
+ unsigned int len)
+{
+ unsigned int from, to, ffs = chance(1, 2);
+ unsigned char *p = (void *)buf;
+
+ from = prandom_u32() % len;
+ /* Corruption span max to end of write unit */
+ to = min(len, ALIGN(from + 1, c->max_write_size));
+
+ ubifs_warn(c, "filled bytes %u-%u with %s", from, to - 1,
+ ffs ? "0xFFs" : "random data");
+
+ if (ffs)
+ memset(p + from, 0xFF, to - from);
+ else
+ prandom_bytes(p + from, to - from);
+
+ return to;
+}
+
+int dbg_leb_write(struct ubifs_info *c, int lnum, const void *buf,
+ int offs, int len)
+{
+ int err, failing;
+
+ if (c->dbg->pc_happened)
+ return -EROFS;
+
+ failing = power_cut_emulated(c, lnum, 1);
+ if (failing) {
+ len = corrupt_data(c, buf, len);
+ ubifs_warn(c, "actually write %d bytes to LEB %d:%d (the buffer was corrupted)",
+ len, lnum, offs);
+ }
+ err = ubi_leb_write(c->ubi, lnum, buf, offs, len);
+ if (err)
+ return err;
+ if (failing)
+ return -EROFS;
+ return 0;
+}
+
+int dbg_leb_change(struct ubifs_info *c, int lnum, const void *buf,
+ int len)
+{
+ int err;
+
+ if (c->dbg->pc_happened)
+ return -EROFS;
+ if (power_cut_emulated(c, lnum, 1))
+ return -EROFS;
+ err = ubi_leb_change(c->ubi, lnum, buf, len);
+ if (err)
+ return err;
+ if (power_cut_emulated(c, lnum, 1))
+ return -EROFS;
+ return 0;
+}
+
+int dbg_leb_unmap(struct ubifs_info *c, int lnum)
+{
+ int err;
+
+ if (c->dbg->pc_happened)
+ return -EROFS;
+ if (power_cut_emulated(c, lnum, 0))
+ return -EROFS;
+ err = ubi_leb_unmap(c->ubi, lnum);
+ if (err)
+ return err;
+ if (power_cut_emulated(c, lnum, 0))
+ return -EROFS;
+ return 0;
+}
+
+int dbg_leb_map(struct ubifs_info *c, int lnum)
+{
+ int err;
+
+ if (c->dbg->pc_happened)
+ return -EROFS;
+ if (power_cut_emulated(c, lnum, 0))
+ return -EROFS;
+ err = ubi_leb_map(c->ubi, lnum);
+ if (err)
+ return err;
+ if (power_cut_emulated(c, lnum, 0))
+ return -EROFS;
+ return 0;
+}
+
+/*
+ * Root directory for UBIFS stuff in debugfs. Contains sub-directories which
+ * contain the stuff specific to particular file-system mounts.
+ */
+static struct dentry *dfs_rootdir;
+
+static int dfs_file_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return nonseekable_open(inode, file);
+}
+
+/**
+ * provide_user_output - provide output to the user reading a debugfs file.
+ * @val: boolean value for the answer
+ * @u: the buffer to store the answer at
+ * @count: size of the buffer
+ * @ppos: position in the @u output buffer
+ *
+ * This is a simple helper function which stores @val boolean value in the user
+ * buffer when the user reads one of UBIFS debugfs files. Returns amount of
+ * bytes written to @u in case of success and a negative error code in case of
+ * failure.
+ */
+static int provide_user_output(int val, char __user *u, size_t count,
+ loff_t *ppos)
+{
+ char buf[3];
+
+ if (val)
+ buf[0] = '1';
+ else
+ buf[0] = '0';
+ buf[1] = '\n';
+ buf[2] = 0x00;
+
+ return simple_read_from_buffer(u, count, ppos, buf, 2);
+}
+
+static ssize_t dfs_file_read(struct file *file, char __user *u, size_t count,
+ loff_t *ppos)
+{
+ struct dentry *dent = file->f_path.dentry;
+ struct ubifs_info *c = file->private_data;
+ struct ubifs_debug_info *d = c->dbg;
+ int val;
+
+ if (dent == d->dfs_chk_gen)
+ val = d->chk_gen;
+ else if (dent == d->dfs_chk_index)
+ val = d->chk_index;
+ else if (dent == d->dfs_chk_orph)
+ val = d->chk_orph;
+ else if (dent == d->dfs_chk_lprops)
+ val = d->chk_lprops;
+ else if (dent == d->dfs_chk_fs)
+ val = d->chk_fs;
+ else if (dent == d->dfs_tst_rcvry)
+ val = d->tst_rcvry;
+ else if (dent == d->dfs_ro_error)
+ val = c->ro_error;
+ else
+ return -EINVAL;
+
+ return provide_user_output(val, u, count, ppos);
+}
+
+/**
+ * interpret_user_input - interpret user debugfs file input.
+ * @u: user-provided buffer with the input
+ * @count: buffer size
+ *
+ * This is a helper function which interpret user input to a boolean UBIFS
+ * debugfs file. Returns %0 or %1 in case of success and a negative error code
+ * in case of failure.
+ */
+static int interpret_user_input(const char __user *u, size_t count)
+{
+ size_t buf_size;
+ char buf[8];
+
+ buf_size = min_t(size_t, count, (sizeof(buf) - 1));
+ if (copy_from_user(buf, u, buf_size))
+ return -EFAULT;
+
+ if (buf[0] == '1')
+ return 1;
+ else if (buf[0] == '0')
+ return 0;
+
+ return -EINVAL;
+}
+
+static ssize_t dfs_file_write(struct file *file, const char __user *u,
+ size_t count, loff_t *ppos)
+{
+ struct ubifs_info *c = file->private_data;
+ struct ubifs_debug_info *d = c->dbg;
+ struct dentry *dent = file->f_path.dentry;
+ int val;
+
+ /*
+ * TODO: this is racy - the file-system might have already been
+ * unmounted and we'd oops in this case. The plan is to fix it with
+ * help of 'iterate_supers_type()' which we should have in v3.0: when
+ * a debugfs opened, we rember FS's UUID in file->private_data. Then
+ * whenever we access the FS via a debugfs file, we iterate all UBIFS
+ * superblocks and fine the one with the same UUID, and take the
+ * locking right.
+ *
+ * The other way to go suggested by Al Viro is to create a separate
+ * 'ubifs-debug' file-system instead.
+ */
+ if (file->f_path.dentry == d->dfs_dump_lprops) {
+ ubifs_dump_lprops(c);
+ return count;
+ }
+ if (file->f_path.dentry == d->dfs_dump_budg) {
+ ubifs_dump_budg(c, &c->bi);
+ return count;
+ }
+ if (file->f_path.dentry == d->dfs_dump_tnc) {
+ mutex_lock(&c->tnc_mutex);
+ ubifs_dump_tnc(c);
+ mutex_unlock(&c->tnc_mutex);
+ return count;
+ }
+
+ val = interpret_user_input(u, count);
+ if (val < 0)
+ return val;
+
+ if (dent == d->dfs_chk_gen)
+ d->chk_gen = val;
+ else if (dent == d->dfs_chk_index)
+ d->chk_index = val;
+ else if (dent == d->dfs_chk_orph)
+ d->chk_orph = val;
+ else if (dent == d->dfs_chk_lprops)
+ d->chk_lprops = val;
+ else if (dent == d->dfs_chk_fs)
+ d->chk_fs = val;
+ else if (dent == d->dfs_tst_rcvry)
+ d->tst_rcvry = val;
+ else if (dent == d->dfs_ro_error)
+ c->ro_error = !!val;
+ else
+ return -EINVAL;
+
+ return count;
+}
+
+static const struct file_operations dfs_fops = {
+ .open = dfs_file_open,
+ .read = dfs_file_read,
+ .write = dfs_file_write,
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+};
+
+/**
+ * dbg_debugfs_init_fs - initialize debugfs for UBIFS instance.
+ * @c: UBIFS file-system description object
+ *
+ * This function creates all debugfs files for this instance of UBIFS. Returns
+ * zero in case of success and a negative error code in case of failure.
+ *
+ * Note, the only reason we have not merged this function with the
+ * 'ubifs_debugging_init()' function is because it is better to initialize
+ * debugfs interfaces at the very end of the mount process, and remove them at
+ * the very beginning of the mount process.
+ */
+int dbg_debugfs_init_fs(struct ubifs_info *c)
+{
+ int err, n;
+ const char *fname;
+ struct dentry *dent;
+ struct ubifs_debug_info *d = c->dbg;
+
+ if (!IS_ENABLED(CONFIG_DEBUG_FS))
+ return 0;
+
+ n = snprintf(d->dfs_dir_name, UBIFS_DFS_DIR_LEN + 1, UBIFS_DFS_DIR_NAME,
+ c->vi.ubi_num, c->vi.vol_id);
+ if (n == UBIFS_DFS_DIR_LEN) {
+ /* The array size is too small */
+ fname = UBIFS_DFS_DIR_NAME;
+ dent = ERR_PTR(-EINVAL);
+ goto out;
+ }
+
+ fname = d->dfs_dir_name;
+ dent = debugfs_create_dir(fname, dfs_rootdir);
+ if (IS_ERR_OR_NULL(dent))
+ goto out;
+ d->dfs_dir = dent;
+
+ fname = "dump_lprops";
+ dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, c, &dfs_fops);
+ if (IS_ERR_OR_NULL(dent))
+ goto out_remove;
+ d->dfs_dump_lprops = dent;
+
+ fname = "dump_budg";
+ dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, c, &dfs_fops);
+ if (IS_ERR_OR_NULL(dent))
+ goto out_remove;
+ d->dfs_dump_budg = dent;
+
+ fname = "dump_tnc";
+ dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, c, &dfs_fops);
+ if (IS_ERR_OR_NULL(dent))
+ goto out_remove;
+ d->dfs_dump_tnc = dent;
+
+ fname = "chk_general";
+ dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, d->dfs_dir, c,
+ &dfs_fops);
+ if (IS_ERR_OR_NULL(dent))
+ goto out_remove;
+ d->dfs_chk_gen = dent;
+
+ fname = "chk_index";
+ dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, d->dfs_dir, c,
+ &dfs_fops);
+ if (IS_ERR_OR_NULL(dent))
+ goto out_remove;
+ d->dfs_chk_index = dent;
+
+ fname = "chk_orphans";
+ dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, d->dfs_dir, c,
+ &dfs_fops);
+ if (IS_ERR_OR_NULL(dent))
+ goto out_remove;
+ d->dfs_chk_orph = dent;
+
+ fname = "chk_lprops";
+ dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, d->dfs_dir, c,
+ &dfs_fops);
+ if (IS_ERR_OR_NULL(dent))
+ goto out_remove;
+ d->dfs_chk_lprops = dent;
+
+ fname = "chk_fs";
+ dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, d->dfs_dir, c,
+ &dfs_fops);
+ if (IS_ERR_OR_NULL(dent))
+ goto out_remove;
+ d->dfs_chk_fs = dent;
+
+ fname = "tst_recovery";
+ dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, d->dfs_dir, c,
+ &dfs_fops);
+ if (IS_ERR_OR_NULL(dent))
+ goto out_remove;
+ d->dfs_tst_rcvry = dent;
+
+ fname = "ro_error";
+ dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, d->dfs_dir, c,
+ &dfs_fops);
+ if (IS_ERR_OR_NULL(dent))
+ goto out_remove;
+ d->dfs_ro_error = dent;
+
+ return 0;
+
+out_remove:
+ debugfs_remove_recursive(d->dfs_dir);
+out:
+ err = dent ? PTR_ERR(dent) : -ENODEV;
+ ubifs_err(c, "cannot create \"%s\" debugfs file or directory, error %d\n",
+ fname, err);
+ return err;
+}
+
+/**
+ * dbg_debugfs_exit_fs - remove all debugfs files.
+ * @c: UBIFS file-system description object
+ */
+void dbg_debugfs_exit_fs(struct ubifs_info *c)
+{
+ if (IS_ENABLED(CONFIG_DEBUG_FS))
+ debugfs_remove_recursive(c->dbg->dfs_dir);
+}
+
+struct ubifs_global_debug_info ubifs_dbg;
+
+static struct dentry *dfs_chk_gen;
+static struct dentry *dfs_chk_index;
+static struct dentry *dfs_chk_orph;
+static struct dentry *dfs_chk_lprops;
+static struct dentry *dfs_chk_fs;
+static struct dentry *dfs_tst_rcvry;
+
+static ssize_t dfs_global_file_read(struct file *file, char __user *u,
+ size_t count, loff_t *ppos)
+{
+ struct dentry *dent = file->f_path.dentry;
+ int val;
+
+ if (dent == dfs_chk_gen)
+ val = ubifs_dbg.chk_gen;
+ else if (dent == dfs_chk_index)
+ val = ubifs_dbg.chk_index;
+ else if (dent == dfs_chk_orph)
+ val = ubifs_dbg.chk_orph;
+ else if (dent == dfs_chk_lprops)
+ val = ubifs_dbg.chk_lprops;
+ else if (dent == dfs_chk_fs)
+ val = ubifs_dbg.chk_fs;
+ else if (dent == dfs_tst_rcvry)
+ val = ubifs_dbg.tst_rcvry;
+ else
+ return -EINVAL;
+
+ return provide_user_output(val, u, count, ppos);
+}
+
+static ssize_t dfs_global_file_write(struct file *file, const char __user *u,
+ size_t count, loff_t *ppos)
+{
+ struct dentry *dent = file->f_path.dentry;
+ int val;
+
+ val = interpret_user_input(u, count);
+ if (val < 0)
+ return val;
+
+ if (dent == dfs_chk_gen)
+ ubifs_dbg.chk_gen = val;
+ else if (dent == dfs_chk_index)
+ ubifs_dbg.chk_index = val;
+ else if (dent == dfs_chk_orph)
+ ubifs_dbg.chk_orph = val;
+ else if (dent == dfs_chk_lprops)
+ ubifs_dbg.chk_lprops = val;
+ else if (dent == dfs_chk_fs)
+ ubifs_dbg.chk_fs = val;
+ else if (dent == dfs_tst_rcvry)
+ ubifs_dbg.tst_rcvry = val;
+ else
+ return -EINVAL;
+
+ return count;
+}
+
+static const struct file_operations dfs_global_fops = {
+ .read = dfs_global_file_read,
+ .write = dfs_global_file_write,
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+};
+
+/**
+ * dbg_debugfs_init - initialize debugfs file-system.
+ *
+ * UBIFS uses debugfs file-system to expose various debugging knobs to
+ * user-space. This function creates "ubifs" directory in the debugfs
+ * file-system. Returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+int dbg_debugfs_init(void)
+{
+ int err;
+ const char *fname;
+ struct dentry *dent;
+
+ if (!IS_ENABLED(CONFIG_DEBUG_FS))
+ return 0;
+
+ fname = "ubifs";
+ dent = debugfs_create_dir(fname, NULL);
+ if (IS_ERR_OR_NULL(dent))
+ goto out;
+ dfs_rootdir = dent;
+
+ fname = "chk_general";
+ dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, dfs_rootdir, NULL,
+ &dfs_global_fops);
+ if (IS_ERR_OR_NULL(dent))
+ goto out_remove;
+ dfs_chk_gen = dent;
+
+ fname = "chk_index";
+ dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, dfs_rootdir, NULL,
+ &dfs_global_fops);
+ if (IS_ERR_OR_NULL(dent))
+ goto out_remove;
+ dfs_chk_index = dent;
+
+ fname = "chk_orphans";
+ dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, dfs_rootdir, NULL,
+ &dfs_global_fops);
+ if (IS_ERR_OR_NULL(dent))
+ goto out_remove;
+ dfs_chk_orph = dent;
+
+ fname = "chk_lprops";
+ dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, dfs_rootdir, NULL,
+ &dfs_global_fops);
+ if (IS_ERR_OR_NULL(dent))
+ goto out_remove;
+ dfs_chk_lprops = dent;
+
+ fname = "chk_fs";
+ dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, dfs_rootdir, NULL,
+ &dfs_global_fops);
+ if (IS_ERR_OR_NULL(dent))
+ goto out_remove;
+ dfs_chk_fs = dent;
+
+ fname = "tst_recovery";
+ dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, dfs_rootdir, NULL,
+ &dfs_global_fops);
+ if (IS_ERR_OR_NULL(dent))
+ goto out_remove;
+ dfs_tst_rcvry = dent;
+
+ return 0;
+
+out_remove:
+ debugfs_remove_recursive(dfs_rootdir);
+out:
+ err = dent ? PTR_ERR(dent) : -ENODEV;
+ pr_err("UBIFS error (pid %d): cannot create \"%s\" debugfs file or directory, error %d\n",
+ current->pid, fname, err);
+ return err;
+}
+
+/**
+ * dbg_debugfs_exit - remove the "ubifs" directory from debugfs file-system.
+ */
+void dbg_debugfs_exit(void)
+{
+ if (IS_ENABLED(CONFIG_DEBUG_FS))
+ debugfs_remove_recursive(dfs_rootdir);
+}
+
+/**
+ * ubifs_debugging_init - initialize UBIFS debugging.
+ * @c: UBIFS file-system description object
+ *
+ * This function initializes debugging-related data for the file system.
+ * Returns zero in case of success and a negative error code in case of
+ * failure.
+ */
+int ubifs_debugging_init(struct ubifs_info *c)
+{
+ c->dbg = kzalloc(sizeof(struct ubifs_debug_info), GFP_KERNEL);
+ if (!c->dbg)
+ return -ENOMEM;
+
+ return 0;
+}
+
+/**
+ * ubifs_debugging_exit - free debugging data.
+ * @c: UBIFS file-system description object
+ */
+void ubifs_debugging_exit(struct ubifs_info *c)
+{
+ kfree(c->dbg);
+}
+#endif
diff --git a/roms/u-boot/fs/ubifs/debug.h b/roms/u-boot/fs/ubifs/debug.h
new file mode 100644
index 000000000..5f6e12702
--- /dev/null
+++ b/roms/u-boot/fs/ubifs/debug.h
@@ -0,0 +1,369 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ *
+ * Authors: Artem Bityutskiy (Битюцкий Артём)
+ * Adrian Hunter
+ */
+
+#ifndef __UBIFS_DEBUG_H__
+#define __UBIFS_DEBUG_H__
+
+/* Checking helper functions */
+typedef int (*dbg_leaf_callback)(struct ubifs_info *c,
+ struct ubifs_zbranch *zbr, void *priv);
+typedef int (*dbg_znode_callback)(struct ubifs_info *c,
+ struct ubifs_znode *znode, void *priv);
+
+/*
+ * The UBIFS debugfs directory name pattern and maximum name length (3 for "ubi"
+ * + 1 for "_" and plus 2x2 for 2 UBI numbers and 1 for the trailing zero byte.
+ */
+#define UBIFS_DFS_DIR_NAME "ubi%d_%d"
+#define UBIFS_DFS_DIR_LEN (3 + 1 + 2*2 + 1)
+
+/**
+ * ubifs_debug_info - per-FS debugging information.
+ * @old_zroot: old index root - used by 'dbg_check_old_index()'
+ * @old_zroot_level: old index root level - used by 'dbg_check_old_index()'
+ * @old_zroot_sqnum: old index root sqnum - used by 'dbg_check_old_index()'
+ *
+ * @pc_happened: non-zero if an emulated power cut happened
+ * @pc_delay: 0=>don't delay, 1=>delay a time, 2=>delay a number of calls
+ * @pc_timeout: time in jiffies when delay of failure mode expires
+ * @pc_cnt: current number of calls to failure mode I/O functions
+ * @pc_cnt_max: number of calls by which to delay failure mode
+ *
+ * @chk_lpt_sz: used by LPT tree size checker
+ * @chk_lpt_sz2: used by LPT tree size checker
+ * @chk_lpt_wastage: used by LPT tree size checker
+ * @chk_lpt_lebs: used by LPT tree size checker
+ * @new_nhead_offs: used by LPT tree size checker
+ * @new_ihead_lnum: used by debugging to check @c->ihead_lnum
+ * @new_ihead_offs: used by debugging to check @c->ihead_offs
+ *
+ * @saved_lst: saved lprops statistics (used by 'dbg_save_space_info()')
+ * @saved_bi: saved budgeting information
+ * @saved_free: saved amount of free space
+ * @saved_idx_gc_cnt: saved value of @c->idx_gc_cnt
+ *
+ * @chk_gen: if general extra checks are enabled
+ * @chk_index: if index xtra checks are enabled
+ * @chk_orph: if orphans extra checks are enabled
+ * @chk_lprops: if lprops extra checks are enabled
+ * @chk_fs: if UBIFS contents extra checks are enabled
+ * @tst_rcvry: if UBIFS recovery testing mode enabled
+ *
+ * @dfs_dir_name: name of debugfs directory containing this file-system's files
+ * @dfs_dir: direntry object of the file-system debugfs directory
+ * @dfs_dump_lprops: "dump lprops" debugfs knob
+ * @dfs_dump_budg: "dump budgeting information" debugfs knob
+ * @dfs_dump_tnc: "dump TNC" debugfs knob
+ * @dfs_chk_gen: debugfs knob to enable UBIFS general extra checks
+ * @dfs_chk_index: debugfs knob to enable UBIFS index extra checks
+ * @dfs_chk_orph: debugfs knob to enable UBIFS orphans extra checks
+ * @dfs_chk_lprops: debugfs knob to enable UBIFS LEP properties extra checks
+ * @dfs_chk_fs: debugfs knob to enable UBIFS contents extra checks
+ * @dfs_tst_rcvry: debugfs knob to enable UBIFS recovery testing
+ * @dfs_ro_error: debugfs knob to switch UBIFS to R/O mode (different to
+ * re-mounting to R/O mode because it does not flush any buffers
+ * and UBIFS just starts returning -EROFS on all write
+ * operations)
+ */
+struct ubifs_debug_info {
+ struct ubifs_zbranch old_zroot;
+ int old_zroot_level;
+ unsigned long long old_zroot_sqnum;
+
+ int pc_happened;
+ int pc_delay;
+ unsigned long pc_timeout;
+ unsigned int pc_cnt;
+ unsigned int pc_cnt_max;
+
+ long long chk_lpt_sz;
+ long long chk_lpt_sz2;
+ long long chk_lpt_wastage;
+ int chk_lpt_lebs;
+ int new_nhead_offs;
+ int new_ihead_lnum;
+ int new_ihead_offs;
+
+ struct ubifs_lp_stats saved_lst;
+ struct ubifs_budg_info saved_bi;
+ long long saved_free;
+ int saved_idx_gc_cnt;
+
+ unsigned int chk_gen:1;
+ unsigned int chk_index:1;
+ unsigned int chk_orph:1;
+ unsigned int chk_lprops:1;
+ unsigned int chk_fs:1;
+ unsigned int tst_rcvry:1;
+
+ char dfs_dir_name[UBIFS_DFS_DIR_LEN + 1];
+ struct dentry *dfs_dir;
+ struct dentry *dfs_dump_lprops;
+ struct dentry *dfs_dump_budg;
+ struct dentry *dfs_dump_tnc;
+ struct dentry *dfs_chk_gen;
+ struct dentry *dfs_chk_index;
+ struct dentry *dfs_chk_orph;
+ struct dentry *dfs_chk_lprops;
+ struct dentry *dfs_chk_fs;
+ struct dentry *dfs_tst_rcvry;
+ struct dentry *dfs_ro_error;
+};
+
+/**
+ * ubifs_global_debug_info - global (not per-FS) UBIFS debugging information.
+ *
+ * @chk_gen: if general extra checks are enabled
+ * @chk_index: if index xtra checks are enabled
+ * @chk_orph: if orphans extra checks are enabled
+ * @chk_lprops: if lprops extra checks are enabled
+ * @chk_fs: if UBIFS contents extra checks are enabled
+ * @tst_rcvry: if UBIFS recovery testing mode enabled
+ */
+struct ubifs_global_debug_info {
+ unsigned int chk_gen:1;
+ unsigned int chk_index:1;
+ unsigned int chk_orph:1;
+ unsigned int chk_lprops:1;
+ unsigned int chk_fs:1;
+ unsigned int tst_rcvry:1;
+};
+
+#ifndef __UBOOT__
+#define ubifs_assert(expr) do { \
+ if (unlikely(!(expr))) { \
+ pr_crit("UBIFS assert failed in %s at %u (pid %d)\n", \
+ __func__, __LINE__, current->pid); \
+ dump_stack(); \
+ } \
+} while (0)
+
+#define ubifs_assert_cmt_locked(c) do { \
+ if (unlikely(down_write_trylock(&(c)->commit_sem))) { \
+ up_write(&(c)->commit_sem); \
+ pr_crit("commit lock is not locked!\n"); \
+ ubifs_assert(0); \
+ } \
+} while (0)
+
+#define ubifs_dbg_msg(type, fmt, ...) \
+ pr_debug("UBIFS DBG " type " (pid %d): " fmt "\n", current->pid, \
+ ##__VA_ARGS__)
+
+#define DBG_KEY_BUF_LEN 48
+#define ubifs_dbg_msg_key(type, key, fmt, ...) do { \
+ char __tmp_key_buf[DBG_KEY_BUF_LEN]; \
+ pr_debug("UBIFS DBG " type " (pid %d): " fmt "%s\n", current->pid, \
+ ##__VA_ARGS__, \
+ dbg_snprintf_key(c, key, __tmp_key_buf, DBG_KEY_BUF_LEN)); \
+} while (0)
+#else
+#define ubifs_assert(expr) do { \
+ if (unlikely(!(expr))) { \
+ pr_debug("UBIFS assert failed in %s at %u\n", \
+ __func__, __LINE__); \
+ dump_stack(); \
+ } \
+} while (0)
+
+#define ubifs_assert_cmt_locked(c) do { \
+ if (unlikely(down_write_trylock(&(c)->commit_sem))) { \
+ up_write(&(c)->commit_sem); \
+ pr_debug("commit lock is not locked!\n"); \
+ ubifs_assert(0); \
+ } \
+} while (0)
+
+#define ubifs_dbg_msg(type, fmt, ...) \
+ pr_debug("UBIFS DBG " type ": " fmt "\n", \
+ ##__VA_ARGS__)
+
+#define DBG_KEY_BUF_LEN 48
+#define ubifs_dbg_msg_key(type, key, fmt, ...) do { \
+ char __tmp_key_buf[DBG_KEY_BUF_LEN]; \
+ pr_debug("UBIFS DBG " type ": " fmt "%s\n", \
+ ##__VA_ARGS__, \
+ dbg_snprintf_key(c, key, __tmp_key_buf, DBG_KEY_BUF_LEN)); \
+} while (0)
+
+#endif
+
+/* General messages */
+#define dbg_gen(fmt, ...) ubifs_dbg_msg("gen", fmt, ##__VA_ARGS__)
+/* Additional journal messages */
+#define dbg_jnl(fmt, ...) ubifs_dbg_msg("jnl", fmt, ##__VA_ARGS__)
+#define dbg_jnlk(key, fmt, ...) \
+ ubifs_dbg_msg_key("jnl", key, fmt, ##__VA_ARGS__)
+/* Additional TNC messages */
+#define dbg_tnc(fmt, ...) ubifs_dbg_msg("tnc", fmt, ##__VA_ARGS__)
+#define dbg_tnck(key, fmt, ...) \
+ ubifs_dbg_msg_key("tnc", key, fmt, ##__VA_ARGS__)
+/* Additional lprops messages */
+#define dbg_lp(fmt, ...) ubifs_dbg_msg("lp", fmt, ##__VA_ARGS__)
+/* Additional LEB find messages */
+#define dbg_find(fmt, ...) ubifs_dbg_msg("find", fmt, ##__VA_ARGS__)
+/* Additional mount messages */
+#define dbg_mnt(fmt, ...) ubifs_dbg_msg("mnt", fmt, ##__VA_ARGS__)
+#define dbg_mntk(key, fmt, ...) \
+ ubifs_dbg_msg_key("mnt", key, fmt, ##__VA_ARGS__)
+/* Additional I/O messages */
+#define dbg_io(fmt, ...) ubifs_dbg_msg("io", fmt, ##__VA_ARGS__)
+/* Additional commit messages */
+#define dbg_cmt(fmt, ...) ubifs_dbg_msg("cmt", fmt, ##__VA_ARGS__)
+/* Additional budgeting messages */
+#define dbg_budg(fmt, ...) ubifs_dbg_msg("budg", fmt, ##__VA_ARGS__)
+/* Additional log messages */
+#define dbg_log(fmt, ...) ubifs_dbg_msg("log", fmt, ##__VA_ARGS__)
+/* Additional gc messages */
+#define dbg_gc(fmt, ...) ubifs_dbg_msg("gc", fmt, ##__VA_ARGS__)
+/* Additional scan messages */
+#define dbg_scan(fmt, ...) ubifs_dbg_msg("scan", fmt, ##__VA_ARGS__)
+/* Additional recovery messages */
+#define dbg_rcvry(fmt, ...) ubifs_dbg_msg("rcvry", fmt, ##__VA_ARGS__)
+
+#ifndef __UBOOT__
+extern struct ubifs_global_debug_info ubifs_dbg;
+
+static inline int dbg_is_chk_gen(const struct ubifs_info *c)
+{
+ return !!(ubifs_dbg.chk_gen || c->dbg->chk_gen);
+}
+static inline int dbg_is_chk_index(const struct ubifs_info *c)
+{
+ return !!(ubifs_dbg.chk_index || c->dbg->chk_index);
+}
+static inline int dbg_is_chk_orph(const struct ubifs_info *c)
+{
+ return !!(ubifs_dbg.chk_orph || c->dbg->chk_orph);
+}
+static inline int dbg_is_chk_lprops(const struct ubifs_info *c)
+{
+ return !!(ubifs_dbg.chk_lprops || c->dbg->chk_lprops);
+}
+static inline int dbg_is_chk_fs(const struct ubifs_info *c)
+{
+ return !!(ubifs_dbg.chk_fs || c->dbg->chk_fs);
+}
+static inline int dbg_is_tst_rcvry(const struct ubifs_info *c)
+{
+ return !!(ubifs_dbg.tst_rcvry || c->dbg->tst_rcvry);
+}
+static inline int dbg_is_power_cut(const struct ubifs_info *c)
+{
+ return !!c->dbg->pc_happened;
+}
+
+int ubifs_debugging_init(struct ubifs_info *c);
+void ubifs_debugging_exit(struct ubifs_info *c);
+#else
+static inline int dbg_is_chk_gen(const struct ubifs_info *c)
+{
+ return 0;
+}
+static inline int dbg_is_chk_index(const struct ubifs_info *c)
+{
+ return 0;
+}
+static inline int dbg_is_chk_orph(const struct ubifs_info *c)
+{
+ return 0;
+}
+static inline int dbg_is_chk_lprops(const struct ubifs_info *c)
+{
+ return 0;
+}
+static inline int dbg_is_chk_fs(const struct ubifs_info *c)
+{
+ return 0;
+}
+static inline int dbg_is_tst_rcvry(const struct ubifs_info *c)
+{
+ return 0;
+}
+static inline int dbg_is_power_cut(const struct ubifs_info *c)
+{
+ return 0;
+}
+
+int ubifs_debugging_init(struct ubifs_info *c);
+void ubifs_debugging_exit(struct ubifs_info *c);
+
+#endif
+
+/* Dump functions */
+const char *dbg_ntype(int type);
+const char *dbg_cstate(int cmt_state);
+const char *dbg_jhead(int jhead);
+const char *dbg_get_key_dump(const struct ubifs_info *c,
+ const union ubifs_key *key);
+const char *dbg_snprintf_key(const struct ubifs_info *c,
+ const union ubifs_key *key, char *buffer, int len);
+void ubifs_dump_inode(struct ubifs_info *c, const struct inode *inode);
+void ubifs_dump_node(const struct ubifs_info *c, const void *node);
+void ubifs_dump_budget_req(const struct ubifs_budget_req *req);
+void ubifs_dump_lstats(const struct ubifs_lp_stats *lst);
+void ubifs_dump_budg(struct ubifs_info *c, const struct ubifs_budg_info *bi);
+void ubifs_dump_lprop(const struct ubifs_info *c,
+ const struct ubifs_lprops *lp);
+void ubifs_dump_lprops(struct ubifs_info *c);
+void ubifs_dump_lpt_info(struct ubifs_info *c);
+void ubifs_dump_leb(const struct ubifs_info *c, int lnum);
+void ubifs_dump_sleb(const struct ubifs_info *c,
+ const struct ubifs_scan_leb *sleb, int offs);
+void ubifs_dump_znode(const struct ubifs_info *c,
+ const struct ubifs_znode *znode);
+void ubifs_dump_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap,
+ int cat);
+void ubifs_dump_pnode(struct ubifs_info *c, struct ubifs_pnode *pnode,
+ struct ubifs_nnode *parent, int iip);
+void ubifs_dump_tnc(struct ubifs_info *c);
+void ubifs_dump_index(struct ubifs_info *c);
+void ubifs_dump_lpt_lebs(const struct ubifs_info *c);
+
+int dbg_walk_index(struct ubifs_info *c, dbg_leaf_callback leaf_cb,
+ dbg_znode_callback znode_cb, void *priv);
+
+/* Checking functions */
+void dbg_save_space_info(struct ubifs_info *c);
+int dbg_check_space_info(struct ubifs_info *c);
+int dbg_check_lprops(struct ubifs_info *c);
+int dbg_old_index_check_init(struct ubifs_info *c, struct ubifs_zbranch *zroot);
+int dbg_check_old_index(struct ubifs_info *c, struct ubifs_zbranch *zroot);
+int dbg_check_cats(struct ubifs_info *c);
+int dbg_check_ltab(struct ubifs_info *c);
+int dbg_chk_lpt_free_spc(struct ubifs_info *c);
+int dbg_chk_lpt_sz(struct ubifs_info *c, int action, int len);
+int dbg_check_synced_i_size(const struct ubifs_info *c, struct inode *inode);
+int dbg_check_dir(struct ubifs_info *c, const struct inode *dir);
+int dbg_check_tnc(struct ubifs_info *c, int extra);
+int dbg_check_idx_size(struct ubifs_info *c, long long idx_size);
+int dbg_check_filesystem(struct ubifs_info *c);
+void dbg_check_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap, int cat,
+ int add_pos);
+int dbg_check_lpt_nodes(struct ubifs_info *c, struct ubifs_cnode *cnode,
+ int row, int col);
+int dbg_check_inode_size(struct ubifs_info *c, const struct inode *inode,
+ loff_t size);
+int dbg_check_data_nodes_order(struct ubifs_info *c, struct list_head *head);
+int dbg_check_nondata_nodes_order(struct ubifs_info *c, struct list_head *head);
+
+int dbg_leb_write(struct ubifs_info *c, int lnum, const void *buf, int offs,
+ int len);
+int dbg_leb_change(struct ubifs_info *c, int lnum, const void *buf, int len);
+int dbg_leb_unmap(struct ubifs_info *c, int lnum);
+int dbg_leb_map(struct ubifs_info *c, int lnum);
+
+/* Debugfs-related stuff */
+int dbg_debugfs_init(void);
+void dbg_debugfs_exit(void);
+int dbg_debugfs_init_fs(struct ubifs_info *c);
+void dbg_debugfs_exit_fs(struct ubifs_info *c);
+
+#endif /* !__UBIFS_DEBUG_H__ */
diff --git a/roms/u-boot/fs/ubifs/gc.c b/roms/u-boot/fs/ubifs/gc.c
new file mode 100644
index 000000000..6a4ada62c
--- /dev/null
+++ b/roms/u-boot/fs/ubifs/gc.c
@@ -0,0 +1,977 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ *
+ * Authors: Adrian Hunter
+ * Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * This file implements garbage collection. The procedure for garbage collection
+ * is different depending on whether a LEB as an index LEB (contains index
+ * nodes) or not. For non-index LEBs, garbage collection finds a LEB which
+ * contains a lot of dirty space (obsolete nodes), and copies the non-obsolete
+ * nodes to the journal, at which point the garbage-collected LEB is free to be
+ * reused. For index LEBs, garbage collection marks the non-obsolete index nodes
+ * dirty in the TNC, and after the next commit, the garbage-collected LEB is
+ * to be reused. Garbage collection will cause the number of dirty index nodes
+ * to grow, however sufficient space is reserved for the index to ensure the
+ * commit will never run out of space.
+ *
+ * Notes about dead watermark. At current UBIFS implementation we assume that
+ * LEBs which have less than @c->dead_wm bytes of free + dirty space are full
+ * and not worth garbage-collecting. The dead watermark is one min. I/O unit
+ * size, or min. UBIFS node size, depending on what is greater. Indeed, UBIFS
+ * Garbage Collector has to synchronize the GC head's write buffer before
+ * returning, so this is about wasting one min. I/O unit. However, UBIFS GC can
+ * actually reclaim even very small pieces of dirty space by garbage collecting
+ * enough dirty LEBs, but we do not bother doing this at this implementation.
+ *
+ * Notes about dark watermark. The results of GC work depends on how big are
+ * the UBIFS nodes GC deals with. Large nodes make GC waste more space. Indeed,
+ * if GC move data from LEB A to LEB B and nodes in LEB A are large, GC would
+ * have to waste large pieces of free space at the end of LEB B, because nodes
+ * from LEB A would not fit. And the worst situation is when all nodes are of
+ * maximum size. So dark watermark is the amount of free + dirty space in LEB
+ * which are guaranteed to be reclaimable. If LEB has less space, the GC might
+ * be unable to reclaim it. So, LEBs with free + dirty greater than dark
+ * watermark are "good" LEBs from GC's point of few. The other LEBs are not so
+ * good, and GC takes extra care when moving them.
+ */
+#ifndef __UBOOT__
+#include <log.h>
+#include <dm/devres.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/list_sort.h>
+#endif
+#include "ubifs.h"
+
+#ifndef __UBOOT__
+/*
+ * GC may need to move more than one LEB to make progress. The below constants
+ * define "soft" and "hard" limits on the number of LEBs the garbage collector
+ * may move.
+ */
+#define SOFT_LEBS_LIMIT 4
+#define HARD_LEBS_LIMIT 32
+
+/**
+ * switch_gc_head - switch the garbage collection journal head.
+ * @c: UBIFS file-system description object
+ * @buf: buffer to write
+ * @len: length of the buffer to write
+ * @lnum: LEB number written is returned here
+ * @offs: offset written is returned here
+ *
+ * This function switch the GC head to the next LEB which is reserved in
+ * @c->gc_lnum. Returns %0 in case of success, %-EAGAIN if commit is required,
+ * and other negative error code in case of failures.
+ */
+static int switch_gc_head(struct ubifs_info *c)
+{
+ int err, gc_lnum = c->gc_lnum;
+ struct ubifs_wbuf *wbuf = &c->jheads[GCHD].wbuf;
+
+ ubifs_assert(gc_lnum != -1);
+ dbg_gc("switch GC head from LEB %d:%d to LEB %d (waste %d bytes)",
+ wbuf->lnum, wbuf->offs + wbuf->used, gc_lnum,
+ c->leb_size - wbuf->offs - wbuf->used);
+
+ err = ubifs_wbuf_sync_nolock(wbuf);
+ if (err)
+ return err;
+
+ /*
+ * The GC write-buffer was synchronized, we may safely unmap
+ * 'c->gc_lnum'.
+ */
+ err = ubifs_leb_unmap(c, gc_lnum);
+ if (err)
+ return err;
+
+ err = ubifs_wbuf_sync_nolock(wbuf);
+ if (err)
+ return err;
+
+ err = ubifs_add_bud_to_log(c, GCHD, gc_lnum, 0);
+ if (err)
+ return err;
+
+ c->gc_lnum = -1;
+ err = ubifs_wbuf_seek_nolock(wbuf, gc_lnum, 0);
+ return err;
+}
+
+/**
+ * data_nodes_cmp - compare 2 data nodes.
+ * @priv: UBIFS file-system description object
+ * @a: first data node
+ * @a: second data node
+ *
+ * This function compares data nodes @a and @b. Returns %1 if @a has greater
+ * inode or block number, and %-1 otherwise.
+ */
+static int data_nodes_cmp(void *priv, struct list_head *a, struct list_head *b)
+{
+ ino_t inuma, inumb;
+ struct ubifs_info *c = priv;
+ struct ubifs_scan_node *sa, *sb;
+
+ cond_resched();
+ if (a == b)
+ return 0;
+
+ sa = list_entry(a, struct ubifs_scan_node, list);
+ sb = list_entry(b, struct ubifs_scan_node, list);
+
+ ubifs_assert(key_type(c, &sa->key) == UBIFS_DATA_KEY);
+ ubifs_assert(key_type(c, &sb->key) == UBIFS_DATA_KEY);
+ ubifs_assert(sa->type == UBIFS_DATA_NODE);
+ ubifs_assert(sb->type == UBIFS_DATA_NODE);
+
+ inuma = key_inum(c, &sa->key);
+ inumb = key_inum(c, &sb->key);
+
+ if (inuma == inumb) {
+ unsigned int blka = key_block(c, &sa->key);
+ unsigned int blkb = key_block(c, &sb->key);
+
+ if (blka <= blkb)
+ return -1;
+ } else if (inuma <= inumb)
+ return -1;
+
+ return 1;
+}
+
+/*
+ * nondata_nodes_cmp - compare 2 non-data nodes.
+ * @priv: UBIFS file-system description object
+ * @a: first node
+ * @a: second node
+ *
+ * This function compares nodes @a and @b. It makes sure that inode nodes go
+ * first and sorted by length in descending order. Directory entry nodes go
+ * after inode nodes and are sorted in ascending hash valuer order.
+ */
+static int nondata_nodes_cmp(void *priv, struct list_head *a,
+ struct list_head *b)
+{
+ ino_t inuma, inumb;
+ struct ubifs_info *c = priv;
+ struct ubifs_scan_node *sa, *sb;
+
+ cond_resched();
+ if (a == b)
+ return 0;
+
+ sa = list_entry(a, struct ubifs_scan_node, list);
+ sb = list_entry(b, struct ubifs_scan_node, list);
+
+ ubifs_assert(key_type(c, &sa->key) != UBIFS_DATA_KEY &&
+ key_type(c, &sb->key) != UBIFS_DATA_KEY);
+ ubifs_assert(sa->type != UBIFS_DATA_NODE &&
+ sb->type != UBIFS_DATA_NODE);
+
+ /* Inodes go before directory entries */
+ if (sa->type == UBIFS_INO_NODE) {
+ if (sb->type == UBIFS_INO_NODE)
+ return sb->len - sa->len;
+ return -1;
+ }
+ if (sb->type == UBIFS_INO_NODE)
+ return 1;
+
+ ubifs_assert(key_type(c, &sa->key) == UBIFS_DENT_KEY ||
+ key_type(c, &sa->key) == UBIFS_XENT_KEY);
+ ubifs_assert(key_type(c, &sb->key) == UBIFS_DENT_KEY ||
+ key_type(c, &sb->key) == UBIFS_XENT_KEY);
+ ubifs_assert(sa->type == UBIFS_DENT_NODE ||
+ sa->type == UBIFS_XENT_NODE);
+ ubifs_assert(sb->type == UBIFS_DENT_NODE ||
+ sb->type == UBIFS_XENT_NODE);
+
+ inuma = key_inum(c, &sa->key);
+ inumb = key_inum(c, &sb->key);
+
+ if (inuma == inumb) {
+ uint32_t hasha = key_hash(c, &sa->key);
+ uint32_t hashb = key_hash(c, &sb->key);
+
+ if (hasha <= hashb)
+ return -1;
+ } else if (inuma <= inumb)
+ return -1;
+
+ return 1;
+}
+
+/**
+ * sort_nodes - sort nodes for GC.
+ * @c: UBIFS file-system description object
+ * @sleb: describes nodes to sort and contains the result on exit
+ * @nondata: contains non-data nodes on exit
+ * @min: minimum node size is returned here
+ *
+ * This function sorts the list of inodes to garbage collect. First of all, it
+ * kills obsolete nodes and separates data and non-data nodes to the
+ * @sleb->nodes and @nondata lists correspondingly.
+ *
+ * Data nodes are then sorted in block number order - this is important for
+ * bulk-read; data nodes with lower inode number go before data nodes with
+ * higher inode number, and data nodes with lower block number go before data
+ * nodes with higher block number;
+ *
+ * Non-data nodes are sorted as follows.
+ * o First go inode nodes - they are sorted in descending length order.
+ * o Then go directory entry nodes - they are sorted in hash order, which
+ * should supposedly optimize 'readdir()'. Direntry nodes with lower parent
+ * inode number go before direntry nodes with higher parent inode number,
+ * and direntry nodes with lower name hash values go before direntry nodes
+ * with higher name hash values.
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+static int sort_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb,
+ struct list_head *nondata, int *min)
+{
+ int err;
+ struct ubifs_scan_node *snod, *tmp;
+
+ *min = INT_MAX;
+
+ /* Separate data nodes and non-data nodes */
+ list_for_each_entry_safe(snod, tmp, &sleb->nodes, list) {
+ ubifs_assert(snod->type == UBIFS_INO_NODE ||
+ snod->type == UBIFS_DATA_NODE ||
+ snod->type == UBIFS_DENT_NODE ||
+ snod->type == UBIFS_XENT_NODE ||
+ snod->type == UBIFS_TRUN_NODE);
+
+ if (snod->type != UBIFS_INO_NODE &&
+ snod->type != UBIFS_DATA_NODE &&
+ snod->type != UBIFS_DENT_NODE &&
+ snod->type != UBIFS_XENT_NODE) {
+ /* Probably truncation node, zap it */
+ list_del(&snod->list);
+ kfree(snod);
+ continue;
+ }
+
+ ubifs_assert(key_type(c, &snod->key) == UBIFS_DATA_KEY ||
+ key_type(c, &snod->key) == UBIFS_INO_KEY ||
+ key_type(c, &snod->key) == UBIFS_DENT_KEY ||
+ key_type(c, &snod->key) == UBIFS_XENT_KEY);
+
+ err = ubifs_tnc_has_node(c, &snod->key, 0, sleb->lnum,
+ snod->offs, 0);
+ if (err < 0)
+ return err;
+
+ if (!err) {
+ /* The node is obsolete, remove it from the list */
+ list_del(&snod->list);
+ kfree(snod);
+ continue;
+ }
+
+ if (snod->len < *min)
+ *min = snod->len;
+
+ if (key_type(c, &snod->key) != UBIFS_DATA_KEY)
+ list_move_tail(&snod->list, nondata);
+ }
+
+ /* Sort data and non-data nodes */
+ list_sort(c, &sleb->nodes, &data_nodes_cmp);
+ list_sort(c, nondata, &nondata_nodes_cmp);
+
+ err = dbg_check_data_nodes_order(c, &sleb->nodes);
+ if (err)
+ return err;
+ err = dbg_check_nondata_nodes_order(c, nondata);
+ if (err)
+ return err;
+ return 0;
+}
+
+/**
+ * move_node - move a node.
+ * @c: UBIFS file-system description object
+ * @sleb: describes the LEB to move nodes from
+ * @snod: the mode to move
+ * @wbuf: write-buffer to move node to
+ *
+ * This function moves node @snod to @wbuf, changes TNC correspondingly, and
+ * destroys @snod. Returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+static int move_node(struct ubifs_info *c, struct ubifs_scan_leb *sleb,
+ struct ubifs_scan_node *snod, struct ubifs_wbuf *wbuf)
+{
+ int err, new_lnum = wbuf->lnum, new_offs = wbuf->offs + wbuf->used;
+
+ cond_resched();
+ err = ubifs_wbuf_write_nolock(wbuf, snod->node, snod->len);
+ if (err)
+ return err;
+
+ err = ubifs_tnc_replace(c, &snod->key, sleb->lnum,
+ snod->offs, new_lnum, new_offs,
+ snod->len);
+ list_del(&snod->list);
+ kfree(snod);
+ return err;
+}
+
+/**
+ * move_nodes - move nodes.
+ * @c: UBIFS file-system description object
+ * @sleb: describes the LEB to move nodes from
+ *
+ * This function moves valid nodes from data LEB described by @sleb to the GC
+ * journal head. This function returns zero in case of success, %-EAGAIN if
+ * commit is required, and other negative error codes in case of other
+ * failures.
+ */
+static int move_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb)
+{
+ int err, min;
+ LIST_HEAD(nondata);
+ struct ubifs_wbuf *wbuf = &c->jheads[GCHD].wbuf;
+
+ if (wbuf->lnum == -1) {
+ /*
+ * The GC journal head is not set, because it is the first GC
+ * invocation since mount.
+ */
+ err = switch_gc_head(c);
+ if (err)
+ return err;
+ }
+
+ err = sort_nodes(c, sleb, &nondata, &min);
+ if (err)
+ goto out;
+
+ /* Write nodes to their new location. Use the first-fit strategy */
+ while (1) {
+ int avail;
+ struct ubifs_scan_node *snod, *tmp;
+
+ /* Move data nodes */
+ list_for_each_entry_safe(snod, tmp, &sleb->nodes, list) {
+ avail = c->leb_size - wbuf->offs - wbuf->used;
+ if (snod->len > avail)
+ /*
+ * Do not skip data nodes in order to optimize
+ * bulk-read.
+ */
+ break;
+
+ err = move_node(c, sleb, snod, wbuf);
+ if (err)
+ goto out;
+ }
+
+ /* Move non-data nodes */
+ list_for_each_entry_safe(snod, tmp, &nondata, list) {
+ avail = c->leb_size - wbuf->offs - wbuf->used;
+ if (avail < min)
+ break;
+
+ if (snod->len > avail) {
+ /*
+ * Keep going only if this is an inode with
+ * some data. Otherwise stop and switch the GC
+ * head. IOW, we assume that data-less inode
+ * nodes and direntry nodes are roughly of the
+ * same size.
+ */
+ if (key_type(c, &snod->key) == UBIFS_DENT_KEY ||
+ snod->len == UBIFS_INO_NODE_SZ)
+ break;
+ continue;
+ }
+
+ err = move_node(c, sleb, snod, wbuf);
+ if (err)
+ goto out;
+ }
+
+ if (list_empty(&sleb->nodes) && list_empty(&nondata))
+ break;
+
+ /*
+ * Waste the rest of the space in the LEB and switch to the
+ * next LEB.
+ */
+ err = switch_gc_head(c);
+ if (err)
+ goto out;
+ }
+
+ return 0;
+
+out:
+ list_splice_tail(&nondata, &sleb->nodes);
+ return err;
+}
+
+/**
+ * gc_sync_wbufs - sync write-buffers for GC.
+ * @c: UBIFS file-system description object
+ *
+ * We must guarantee that obsoleting nodes are on flash. Unfortunately they may
+ * be in a write-buffer instead. That is, a node could be written to a
+ * write-buffer, obsoleting another node in a LEB that is GC'd. If that LEB is
+ * erased before the write-buffer is sync'd and then there is an unclean
+ * unmount, then an existing node is lost. To avoid this, we sync all
+ * write-buffers.
+ *
+ * This function returns %0 on success or a negative error code on failure.
+ */
+static int gc_sync_wbufs(struct ubifs_info *c)
+{
+ int err, i;
+
+ for (i = 0; i < c->jhead_cnt; i++) {
+ if (i == GCHD)
+ continue;
+ err = ubifs_wbuf_sync(&c->jheads[i].wbuf);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+/**
+ * ubifs_garbage_collect_leb - garbage-collect a logical eraseblock.
+ * @c: UBIFS file-system description object
+ * @lp: describes the LEB to garbage collect
+ *
+ * This function garbage-collects an LEB and returns one of the @LEB_FREED,
+ * @LEB_RETAINED, etc positive codes in case of success, %-EAGAIN if commit is
+ * required, and other negative error codes in case of failures.
+ */
+int ubifs_garbage_collect_leb(struct ubifs_info *c, struct ubifs_lprops *lp)
+{
+ struct ubifs_scan_leb *sleb;
+ struct ubifs_scan_node *snod;
+ struct ubifs_wbuf *wbuf = &c->jheads[GCHD].wbuf;
+ int err = 0, lnum = lp->lnum;
+
+ ubifs_assert(c->gc_lnum != -1 || wbuf->offs + wbuf->used == 0 ||
+ c->need_recovery);
+ ubifs_assert(c->gc_lnum != lnum);
+ ubifs_assert(wbuf->lnum != lnum);
+
+ if (lp->free + lp->dirty == c->leb_size) {
+ /* Special case - a free LEB */
+ dbg_gc("LEB %d is free, return it", lp->lnum);
+ ubifs_assert(!(lp->flags & LPROPS_INDEX));
+
+ if (lp->free != c->leb_size) {
+ /*
+ * Write buffers must be sync'd before unmapping
+ * freeable LEBs, because one of them may contain data
+ * which obsoletes something in 'lp->pnum'.
+ */
+ err = gc_sync_wbufs(c);
+ if (err)
+ return err;
+ err = ubifs_change_one_lp(c, lp->lnum, c->leb_size,
+ 0, 0, 0, 0);
+ if (err)
+ return err;
+ }
+ err = ubifs_leb_unmap(c, lp->lnum);
+ if (err)
+ return err;
+
+ if (c->gc_lnum == -1) {
+ c->gc_lnum = lnum;
+ return LEB_RETAINED;
+ }
+
+ return LEB_FREED;
+ }
+
+ /*
+ * We scan the entire LEB even though we only really need to scan up to
+ * (c->leb_size - lp->free).
+ */
+ sleb = ubifs_scan(c, lnum, 0, c->sbuf, 0);
+ if (IS_ERR(sleb))
+ return PTR_ERR(sleb);
+
+ ubifs_assert(!list_empty(&sleb->nodes));
+ snod = list_entry(sleb->nodes.next, struct ubifs_scan_node, list);
+
+ if (snod->type == UBIFS_IDX_NODE) {
+ struct ubifs_gced_idx_leb *idx_gc;
+
+ dbg_gc("indexing LEB %d (free %d, dirty %d)",
+ lnum, lp->free, lp->dirty);
+ list_for_each_entry(snod, &sleb->nodes, list) {
+ struct ubifs_idx_node *idx = snod->node;
+ int level = le16_to_cpu(idx->level);
+
+ ubifs_assert(snod->type == UBIFS_IDX_NODE);
+ key_read(c, ubifs_idx_key(c, idx), &snod->key);
+ err = ubifs_dirty_idx_node(c, &snod->key, level, lnum,
+ snod->offs);
+ if (err)
+ goto out;
+ }
+
+ idx_gc = kmalloc(sizeof(struct ubifs_gced_idx_leb), GFP_NOFS);
+ if (!idx_gc) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ idx_gc->lnum = lnum;
+ idx_gc->unmap = 0;
+ list_add(&idx_gc->list, &c->idx_gc);
+
+ /*
+ * Don't release the LEB until after the next commit, because
+ * it may contain data which is needed for recovery. So
+ * although we freed this LEB, it will become usable only after
+ * the commit.
+ */
+ err = ubifs_change_one_lp(c, lnum, c->leb_size, 0, 0,
+ LPROPS_INDEX, 1);
+ if (err)
+ goto out;
+ err = LEB_FREED_IDX;
+ } else {
+ dbg_gc("data LEB %d (free %d, dirty %d)",
+ lnum, lp->free, lp->dirty);
+
+ err = move_nodes(c, sleb);
+ if (err)
+ goto out_inc_seq;
+
+ err = gc_sync_wbufs(c);
+ if (err)
+ goto out_inc_seq;
+
+ err = ubifs_change_one_lp(c, lnum, c->leb_size, 0, 0, 0, 0);
+ if (err)
+ goto out_inc_seq;
+
+ /* Allow for races with TNC */
+ c->gced_lnum = lnum;
+ smp_wmb();
+ c->gc_seq += 1;
+ smp_wmb();
+
+ if (c->gc_lnum == -1) {
+ c->gc_lnum = lnum;
+ err = LEB_RETAINED;
+ } else {
+ err = ubifs_wbuf_sync_nolock(wbuf);
+ if (err)
+ goto out;
+
+ err = ubifs_leb_unmap(c, lnum);
+ if (err)
+ goto out;
+
+ err = LEB_FREED;
+ }
+ }
+
+out:
+ ubifs_scan_destroy(sleb);
+ return err;
+
+out_inc_seq:
+ /* We may have moved at least some nodes so allow for races with TNC */
+ c->gced_lnum = lnum;
+ smp_wmb();
+ c->gc_seq += 1;
+ smp_wmb();
+ goto out;
+}
+
+/**
+ * ubifs_garbage_collect - UBIFS garbage collector.
+ * @c: UBIFS file-system description object
+ * @anyway: do GC even if there are free LEBs
+ *
+ * This function does out-of-place garbage collection. The return codes are:
+ * o positive LEB number if the LEB has been freed and may be used;
+ * o %-EAGAIN if the caller has to run commit;
+ * o %-ENOSPC if GC failed to make any progress;
+ * o other negative error codes in case of other errors.
+ *
+ * Garbage collector writes data to the journal when GC'ing data LEBs, and just
+ * marking indexing nodes dirty when GC'ing indexing LEBs. Thus, at some point
+ * commit may be required. But commit cannot be run from inside GC, because the
+ * caller might be holding the commit lock, so %-EAGAIN is returned instead;
+ * And this error code means that the caller has to run commit, and re-run GC
+ * if there is still no free space.
+ *
+ * There are many reasons why this function may return %-EAGAIN:
+ * o the log is full and there is no space to write an LEB reference for
+ * @c->gc_lnum;
+ * o the journal is too large and exceeds size limitations;
+ * o GC moved indexing LEBs, but they can be used only after the commit;
+ * o the shrinker fails to find clean znodes to free and requests the commit;
+ * o etc.
+ *
+ * Note, if the file-system is close to be full, this function may return
+ * %-EAGAIN infinitely, so the caller has to limit amount of re-invocations of
+ * the function. E.g., this happens if the limits on the journal size are too
+ * tough and GC writes too much to the journal before an LEB is freed. This
+ * might also mean that the journal is too large, and the TNC becomes to big,
+ * so that the shrinker is constantly called, finds not clean znodes to free,
+ * and requests commit. Well, this may also happen if the journal is all right,
+ * but another kernel process consumes too much memory. Anyway, infinite
+ * %-EAGAIN may happen, but in some extreme/misconfiguration cases.
+ */
+int ubifs_garbage_collect(struct ubifs_info *c, int anyway)
+{
+ int i, err, ret, min_space = c->dead_wm;
+ struct ubifs_lprops lp;
+ struct ubifs_wbuf *wbuf = &c->jheads[GCHD].wbuf;
+
+ ubifs_assert_cmt_locked(c);
+ ubifs_assert(!c->ro_media && !c->ro_mount);
+
+ if (ubifs_gc_should_commit(c))
+ return -EAGAIN;
+
+ mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead);
+
+ if (c->ro_error) {
+ ret = -EROFS;
+ goto out_unlock;
+ }
+
+ /* We expect the write-buffer to be empty on entry */
+ ubifs_assert(!wbuf->used);
+
+ for (i = 0; ; i++) {
+ int space_before, space_after;
+
+ cond_resched();
+
+ /* Give the commit an opportunity to run */
+ if (ubifs_gc_should_commit(c)) {
+ ret = -EAGAIN;
+ break;
+ }
+
+ if (i > SOFT_LEBS_LIMIT && !list_empty(&c->idx_gc)) {
+ /*
+ * We've done enough iterations. Indexing LEBs were
+ * moved and will be available after the commit.
+ */
+ dbg_gc("soft limit, some index LEBs GC'ed, -EAGAIN");
+ ubifs_commit_required(c);
+ ret = -EAGAIN;
+ break;
+ }
+
+ if (i > HARD_LEBS_LIMIT) {
+ /*
+ * We've moved too many LEBs and have not made
+ * progress, give up.
+ */
+ dbg_gc("hard limit, -ENOSPC");
+ ret = -ENOSPC;
+ break;
+ }
+
+ /*
+ * Empty and freeable LEBs can turn up while we waited for
+ * the wbuf lock, or while we have been running GC. In that
+ * case, we should just return one of those instead of
+ * continuing to GC dirty LEBs. Hence we request
+ * 'ubifs_find_dirty_leb()' to return an empty LEB if it can.
+ */
+ ret = ubifs_find_dirty_leb(c, &lp, min_space, anyway ? 0 : 1);
+ if (ret) {
+ if (ret == -ENOSPC)
+ dbg_gc("no more dirty LEBs");
+ break;
+ }
+
+ dbg_gc("found LEB %d: free %d, dirty %d, sum %d (min. space %d)",
+ lp.lnum, lp.free, lp.dirty, lp.free + lp.dirty,
+ min_space);
+
+ space_before = c->leb_size - wbuf->offs - wbuf->used;
+ if (wbuf->lnum == -1)
+ space_before = 0;
+
+ ret = ubifs_garbage_collect_leb(c, &lp);
+ if (ret < 0) {
+ if (ret == -EAGAIN) {
+ /*
+ * This is not error, so we have to return the
+ * LEB to lprops. But if 'ubifs_return_leb()'
+ * fails, its failure code is propagated to the
+ * caller instead of the original '-EAGAIN'.
+ */
+ err = ubifs_return_leb(c, lp.lnum);
+ if (err)
+ ret = err;
+ break;
+ }
+ goto out;
+ }
+
+ if (ret == LEB_FREED) {
+ /* An LEB has been freed and is ready for use */
+ dbg_gc("LEB %d freed, return", lp.lnum);
+ ret = lp.lnum;
+ break;
+ }
+
+ if (ret == LEB_FREED_IDX) {
+ /*
+ * This was an indexing LEB and it cannot be
+ * immediately used. And instead of requesting the
+ * commit straight away, we try to garbage collect some
+ * more.
+ */
+ dbg_gc("indexing LEB %d freed, continue", lp.lnum);
+ continue;
+ }
+
+ ubifs_assert(ret == LEB_RETAINED);
+ space_after = c->leb_size - wbuf->offs - wbuf->used;
+ dbg_gc("LEB %d retained, freed %d bytes", lp.lnum,
+ space_after - space_before);
+
+ if (space_after > space_before) {
+ /* GC makes progress, keep working */
+ min_space >>= 1;
+ if (min_space < c->dead_wm)
+ min_space = c->dead_wm;
+ continue;
+ }
+
+ dbg_gc("did not make progress");
+
+ /*
+ * GC moved an LEB bud have not done any progress. This means
+ * that the previous GC head LEB contained too few free space
+ * and the LEB which was GC'ed contained only large nodes which
+ * did not fit that space.
+ *
+ * We can do 2 things:
+ * 1. pick another LEB in a hope it'll contain a small node
+ * which will fit the space we have at the end of current GC
+ * head LEB, but there is no guarantee, so we try this out
+ * unless we have already been working for too long;
+ * 2. request an LEB with more dirty space, which will force
+ * 'ubifs_find_dirty_leb()' to start scanning the lprops
+ * table, instead of just picking one from the heap
+ * (previously it already picked the dirtiest LEB).
+ */
+ if (i < SOFT_LEBS_LIMIT) {
+ dbg_gc("try again");
+ continue;
+ }
+
+ min_space <<= 1;
+ if (min_space > c->dark_wm)
+ min_space = c->dark_wm;
+ dbg_gc("set min. space to %d", min_space);
+ }
+
+ if (ret == -ENOSPC && !list_empty(&c->idx_gc)) {
+ dbg_gc("no space, some index LEBs GC'ed, -EAGAIN");
+ ubifs_commit_required(c);
+ ret = -EAGAIN;
+ }
+
+ err = ubifs_wbuf_sync_nolock(wbuf);
+ if (!err)
+ err = ubifs_leb_unmap(c, c->gc_lnum);
+ if (err) {
+ ret = err;
+ goto out;
+ }
+out_unlock:
+ mutex_unlock(&wbuf->io_mutex);
+ return ret;
+
+out:
+ ubifs_assert(ret < 0);
+ ubifs_assert(ret != -ENOSPC && ret != -EAGAIN);
+ ubifs_wbuf_sync_nolock(wbuf);
+ ubifs_ro_mode(c, ret);
+ mutex_unlock(&wbuf->io_mutex);
+ ubifs_return_leb(c, lp.lnum);
+ return ret;
+}
+
+/**
+ * ubifs_gc_start_commit - garbage collection at start of commit.
+ * @c: UBIFS file-system description object
+ *
+ * If a LEB has only dirty and free space, then we may safely unmap it and make
+ * it free. Note, we cannot do this with indexing LEBs because dirty space may
+ * correspond index nodes that are required for recovery. In that case, the
+ * LEB cannot be unmapped until after the next commit.
+ *
+ * This function returns %0 upon success and a negative error code upon failure.
+ */
+int ubifs_gc_start_commit(struct ubifs_info *c)
+{
+ struct ubifs_gced_idx_leb *idx_gc;
+ const struct ubifs_lprops *lp;
+ int err = 0, flags;
+
+ ubifs_get_lprops(c);
+
+ /*
+ * Unmap (non-index) freeable LEBs. Note that recovery requires that all
+ * wbufs are sync'd before this, which is done in 'do_commit()'.
+ */
+ while (1) {
+ lp = ubifs_fast_find_freeable(c);
+ if (IS_ERR(lp)) {
+ err = PTR_ERR(lp);
+ goto out;
+ }
+ if (!lp)
+ break;
+ ubifs_assert(!(lp->flags & LPROPS_TAKEN));
+ ubifs_assert(!(lp->flags & LPROPS_INDEX));
+ err = ubifs_leb_unmap(c, lp->lnum);
+ if (err)
+ goto out;
+ lp = ubifs_change_lp(c, lp, c->leb_size, 0, lp->flags, 0);
+ if (IS_ERR(lp)) {
+ err = PTR_ERR(lp);
+ goto out;
+ }
+ ubifs_assert(!(lp->flags & LPROPS_TAKEN));
+ ubifs_assert(!(lp->flags & LPROPS_INDEX));
+ }
+
+ /* Mark GC'd index LEBs OK to unmap after this commit finishes */
+ list_for_each_entry(idx_gc, &c->idx_gc, list)
+ idx_gc->unmap = 1;
+
+ /* Record index freeable LEBs for unmapping after commit */
+ while (1) {
+ lp = ubifs_fast_find_frdi_idx(c);
+ if (IS_ERR(lp)) {
+ err = PTR_ERR(lp);
+ goto out;
+ }
+ if (!lp)
+ break;
+ idx_gc = kmalloc(sizeof(struct ubifs_gced_idx_leb), GFP_NOFS);
+ if (!idx_gc) {
+ err = -ENOMEM;
+ goto out;
+ }
+ ubifs_assert(!(lp->flags & LPROPS_TAKEN));
+ ubifs_assert(lp->flags & LPROPS_INDEX);
+ /* Don't release the LEB until after the next commit */
+ flags = (lp->flags | LPROPS_TAKEN) ^ LPROPS_INDEX;
+ lp = ubifs_change_lp(c, lp, c->leb_size, 0, flags, 1);
+ if (IS_ERR(lp)) {
+ err = PTR_ERR(lp);
+ kfree(idx_gc);
+ goto out;
+ }
+ ubifs_assert(lp->flags & LPROPS_TAKEN);
+ ubifs_assert(!(lp->flags & LPROPS_INDEX));
+ idx_gc->lnum = lp->lnum;
+ idx_gc->unmap = 1;
+ list_add(&idx_gc->list, &c->idx_gc);
+ }
+out:
+ ubifs_release_lprops(c);
+ return err;
+}
+
+/**
+ * ubifs_gc_end_commit - garbage collection at end of commit.
+ * @c: UBIFS file-system description object
+ *
+ * This function completes out-of-place garbage collection of index LEBs.
+ */
+int ubifs_gc_end_commit(struct ubifs_info *c)
+{
+ struct ubifs_gced_idx_leb *idx_gc, *tmp;
+ struct ubifs_wbuf *wbuf;
+ int err = 0;
+
+ wbuf = &c->jheads[GCHD].wbuf;
+ mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead);
+ list_for_each_entry_safe(idx_gc, tmp, &c->idx_gc, list)
+ if (idx_gc->unmap) {
+ dbg_gc("LEB %d", idx_gc->lnum);
+ err = ubifs_leb_unmap(c, idx_gc->lnum);
+ if (err)
+ goto out;
+ err = ubifs_change_one_lp(c, idx_gc->lnum, LPROPS_NC,
+ LPROPS_NC, 0, LPROPS_TAKEN, -1);
+ if (err)
+ goto out;
+ list_del(&idx_gc->list);
+ kfree(idx_gc);
+ }
+out:
+ mutex_unlock(&wbuf->io_mutex);
+ return err;
+}
+#endif
+/**
+ * ubifs_destroy_idx_gc - destroy idx_gc list.
+ * @c: UBIFS file-system description object
+ *
+ * This function destroys the @c->idx_gc list. It is called when unmounting
+ * so locks are not needed. Returns zero in case of success and a negative
+ * error code in case of failure.
+ */
+void ubifs_destroy_idx_gc(struct ubifs_info *c)
+{
+ while (!list_empty(&c->idx_gc)) {
+ struct ubifs_gced_idx_leb *idx_gc;
+
+ idx_gc = list_entry(c->idx_gc.next, struct ubifs_gced_idx_leb,
+ list);
+ c->idx_gc_cnt -= 1;
+ list_del(&idx_gc->list);
+ kfree(idx_gc);
+ }
+}
+#ifndef __UBOOT__
+/**
+ * ubifs_get_idx_gc_leb - get a LEB from GC'd index LEB list.
+ * @c: UBIFS file-system description object
+ *
+ * Called during start commit so locks are not needed.
+ */
+int ubifs_get_idx_gc_leb(struct ubifs_info *c)
+{
+ struct ubifs_gced_idx_leb *idx_gc;
+ int lnum;
+
+ if (list_empty(&c->idx_gc))
+ return -ENOSPC;
+ idx_gc = list_entry(c->idx_gc.next, struct ubifs_gced_idx_leb, list);
+ lnum = idx_gc->lnum;
+ /* c->idx_gc_cnt is updated by the caller when lprops are updated */
+ list_del(&idx_gc->list);
+ kfree(idx_gc);
+ return lnum;
+}
+#endif
diff --git a/roms/u-boot/fs/ubifs/io.c b/roms/u-boot/fs/ubifs/io.c
new file mode 100644
index 000000000..9962cbe7e
--- /dev/null
+++ b/roms/u-boot/fs/ubifs/io.c
@@ -0,0 +1,1163 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ * Copyright (C) 2006, 2007 University of Szeged, Hungary
+ *
+ * Authors: Artem Bityutskiy (Битюцкий Артём)
+ * Adrian Hunter
+ * Zoltan Sogor
+ */
+
+/*
+ * This file implements UBIFS I/O subsystem which provides various I/O-related
+ * helper functions (reading/writing/checking/validating nodes) and implements
+ * write-buffering support. Write buffers help to save space which otherwise
+ * would have been wasted for padding to the nearest minimal I/O unit boundary.
+ * Instead, data first goes to the write-buffer and is flushed when the
+ * buffer is full or when it is not used for some time (by timer). This is
+ * similar to the mechanism is used by JFFS2.
+ *
+ * UBIFS distinguishes between minimum write size (@c->min_io_size) and maximum
+ * write size (@c->max_write_size). The latter is the maximum amount of bytes
+ * the underlying flash is able to program at a time, and writing in
+ * @c->max_write_size units should presumably be faster. Obviously,
+ * @c->min_io_size <= @c->max_write_size. Write-buffers are of
+ * @c->max_write_size bytes in size for maximum performance. However, when a
+ * write-buffer is flushed, only the portion of it (aligned to @c->min_io_size
+ * boundary) which contains data is written, not the whole write-buffer,
+ * because this is more space-efficient.
+ *
+ * This optimization adds few complications to the code. Indeed, on the one
+ * hand, we want to write in optimal @c->max_write_size bytes chunks, which
+ * also means aligning writes at the @c->max_write_size bytes offsets. On the
+ * other hand, we do not want to waste space when synchronizing the write
+ * buffer, so during synchronization we writes in smaller chunks. And this makes
+ * the next write offset to be not aligned to @c->max_write_size bytes. So the
+ * have to make sure that the write-buffer offset (@wbuf->offs) becomes aligned
+ * to @c->max_write_size bytes again. We do this by temporarily shrinking
+ * write-buffer size (@wbuf->size).
+ *
+ * Write-buffers are defined by 'struct ubifs_wbuf' objects and protected by
+ * mutexes defined inside these objects. Since sometimes upper-level code
+ * has to lock the write-buffer (e.g. journal space reservation code), many
+ * functions related to write-buffers have "nolock" suffix which means that the
+ * caller has to lock the write-buffer before calling this function.
+ *
+ * UBIFS stores nodes at 64 bit-aligned addresses. If the node length is not
+ * aligned, UBIFS starts the next node from the aligned address, and the padded
+ * bytes may contain any rubbish. In other words, UBIFS does not put padding
+ * bytes in those small gaps. Common headers of nodes store real node lengths,
+ * not aligned lengths. Indexing nodes also store real lengths in branches.
+ *
+ * UBIFS uses padding when it pads to the next min. I/O unit. In this case it
+ * uses padding nodes or padding bytes, if the padding node does not fit.
+ *
+ * All UBIFS nodes are protected by CRC checksums and UBIFS checks CRC when
+ * they are read from the flash media.
+ */
+
+#ifndef __UBOOT__
+#include <init.h>
+#include <log.h>
+#include <dm/devres.h>
+#include <linux/crc32.h>
+#include <linux/slab.h>
+#include <u-boot/crc.h>
+#else
+#include <linux/compat.h>
+#include <linux/err.h>
+#endif
+#include "ubifs.h"
+
+/**
+ * ubifs_ro_mode - switch UBIFS to read read-only mode.
+ * @c: UBIFS file-system description object
+ * @err: error code which is the reason of switching to R/O mode
+ */
+void ubifs_ro_mode(struct ubifs_info *c, int err)
+{
+ if (!c->ro_error) {
+ c->ro_error = 1;
+ c->no_chk_data_crc = 0;
+ c->vfs_sb->s_flags |= MS_RDONLY;
+ ubifs_warn(c, "switched to read-only mode, error %d", err);
+ dump_stack();
+ }
+}
+
+/*
+ * Below are simple wrappers over UBI I/O functions which include some
+ * additional checks and UBIFS debugging stuff. See corresponding UBI function
+ * for more information.
+ */
+
+int ubifs_leb_read(const struct ubifs_info *c, int lnum, void *buf, int offs,
+ int len, int even_ebadmsg)
+{
+ int err;
+
+ err = ubi_read(c->ubi, lnum, buf, offs, len);
+ /*
+ * In case of %-EBADMSG print the error message only if the
+ * @even_ebadmsg is true.
+ */
+ if (err && (err != -EBADMSG || even_ebadmsg)) {
+ ubifs_err(c, "reading %d bytes from LEB %d:%d failed, error %d",
+ len, lnum, offs, err);
+ dump_stack();
+ }
+ return err;
+}
+
+int ubifs_leb_write(struct ubifs_info *c, int lnum, const void *buf, int offs,
+ int len)
+{
+ int err = 0;
+
+ ubifs_assert(!c->ro_media && !c->ro_mount);
+ if (c->ro_error)
+ return -EROFS;
+ if (!dbg_is_tst_rcvry(c))
+ err = ubi_leb_write(c->ubi, lnum, buf, offs, len);
+#ifndef __UBOOT__
+ else
+ err = dbg_leb_write(c, lnum, buf, offs, len);
+#endif
+ if (err) {
+ ubifs_err(c, "writing %d bytes to LEB %d:%d failed, error %d",
+ len, lnum, offs, err);
+ ubifs_ro_mode(c, err);
+ dump_stack();
+ }
+ return err;
+}
+
+int ubifs_leb_change(struct ubifs_info *c, int lnum, const void *buf, int len)
+{
+ int err = 0;
+
+ ubifs_assert(!c->ro_media && !c->ro_mount);
+ if (c->ro_error)
+ return -EROFS;
+ if (!dbg_is_tst_rcvry(c))
+ err = ubi_leb_change(c->ubi, lnum, buf, len);
+#ifndef __UBOOT__
+ else
+ err = dbg_leb_change(c, lnum, buf, len);
+#endif
+ if (err) {
+ ubifs_err(c, "changing %d bytes in LEB %d failed, error %d",
+ len, lnum, err);
+ ubifs_ro_mode(c, err);
+ dump_stack();
+ }
+ return err;
+}
+
+int ubifs_leb_unmap(struct ubifs_info *c, int lnum)
+{
+ int err = 0;
+
+ ubifs_assert(!c->ro_media && !c->ro_mount);
+ if (c->ro_error)
+ return -EROFS;
+ if (!dbg_is_tst_rcvry(c))
+ err = ubi_leb_unmap(c->ubi, lnum);
+#ifndef __UBOOT__
+ else
+ err = dbg_leb_unmap(c, lnum);
+#endif
+ if (err) {
+ ubifs_err(c, "unmap LEB %d failed, error %d", lnum, err);
+ ubifs_ro_mode(c, err);
+ dump_stack();
+ }
+ return err;
+}
+
+int ubifs_leb_map(struct ubifs_info *c, int lnum)
+{
+ int err = 0;
+
+ ubifs_assert(!c->ro_media && !c->ro_mount);
+ if (c->ro_error)
+ return -EROFS;
+ if (!dbg_is_tst_rcvry(c))
+ err = ubi_leb_map(c->ubi, lnum);
+#ifndef __UBOOT__
+ else
+ err = dbg_leb_map(c, lnum);
+#endif
+ if (err) {
+ ubifs_err(c, "mapping LEB %d failed, error %d", lnum, err);
+ ubifs_ro_mode(c, err);
+ dump_stack();
+ }
+ return err;
+}
+
+int ubifs_is_mapped(const struct ubifs_info *c, int lnum)
+{
+ int err;
+
+ err = ubi_is_mapped(c->ubi, lnum);
+ if (err < 0) {
+ ubifs_err(c, "ubi_is_mapped failed for LEB %d, error %d",
+ lnum, err);
+ dump_stack();
+ }
+ return err;
+}
+
+/**
+ * ubifs_check_node - check node.
+ * @c: UBIFS file-system description object
+ * @buf: node to check
+ * @lnum: logical eraseblock number
+ * @offs: offset within the logical eraseblock
+ * @quiet: print no messages
+ * @must_chk_crc: indicates whether to always check the CRC
+ *
+ * This function checks node magic number and CRC checksum. This function also
+ * validates node length to prevent UBIFS from becoming crazy when an attacker
+ * feeds it a file-system image with incorrect nodes. For example, too large
+ * node length in the common header could cause UBIFS to read memory outside of
+ * allocated buffer when checking the CRC checksum.
+ *
+ * This function may skip data nodes CRC checking if @c->no_chk_data_crc is
+ * true, which is controlled by corresponding UBIFS mount option. However, if
+ * @must_chk_crc is true, then @c->no_chk_data_crc is ignored and CRC is
+ * checked. Similarly, if @c->mounting or @c->remounting_rw is true (we are
+ * mounting or re-mounting to R/W mode), @c->no_chk_data_crc is ignored and CRC
+ * is checked. This is because during mounting or re-mounting from R/O mode to
+ * R/W mode we may read journal nodes (when replying the journal or doing the
+ * recovery) and the journal nodes may potentially be corrupted, so checking is
+ * required.
+ *
+ * This function returns zero in case of success and %-EUCLEAN in case of bad
+ * CRC or magic.
+ */
+int ubifs_check_node(const struct ubifs_info *c, const void *buf, int lnum,
+ int offs, int quiet, int must_chk_crc)
+{
+ int err = -EINVAL, type, node_len;
+ uint32_t crc, node_crc, magic;
+ const struct ubifs_ch *ch = buf;
+
+ ubifs_assert(lnum >= 0 && lnum < c->leb_cnt && offs >= 0);
+ ubifs_assert(!(offs & 7) && offs < c->leb_size);
+
+ magic = le32_to_cpu(ch->magic);
+ if (magic != UBIFS_NODE_MAGIC) {
+ if (!quiet)
+ ubifs_err(c, "bad magic %#08x, expected %#08x",
+ magic, UBIFS_NODE_MAGIC);
+ err = -EUCLEAN;
+ goto out;
+ }
+
+ type = ch->node_type;
+ if (type < 0 || type >= UBIFS_NODE_TYPES_CNT) {
+ if (!quiet)
+ ubifs_err(c, "bad node type %d", type);
+ goto out;
+ }
+
+ node_len = le32_to_cpu(ch->len);
+ if (node_len + offs > c->leb_size)
+ goto out_len;
+
+ if (c->ranges[type].max_len == 0) {
+ if (node_len != c->ranges[type].len)
+ goto out_len;
+ } else if (node_len < c->ranges[type].min_len ||
+ node_len > c->ranges[type].max_len)
+ goto out_len;
+
+ if (!must_chk_crc && type == UBIFS_DATA_NODE && !c->mounting &&
+ !c->remounting_rw && c->no_chk_data_crc)
+ return 0;
+
+ crc = crc32(UBIFS_CRC32_INIT, buf + 8, node_len - 8);
+ node_crc = le32_to_cpu(ch->crc);
+ if (crc != node_crc) {
+ if (!quiet)
+ ubifs_err(c, "bad CRC: calculated %#08x, read %#08x",
+ crc, node_crc);
+ err = -EUCLEAN;
+ goto out;
+ }
+
+ return 0;
+
+out_len:
+ if (!quiet)
+ ubifs_err(c, "bad node length %d", node_len);
+out:
+ if (!quiet) {
+ ubifs_err(c, "bad node at LEB %d:%d", lnum, offs);
+ ubifs_dump_node(c, buf);
+ dump_stack();
+ }
+ return err;
+}
+
+/**
+ * ubifs_pad - pad flash space.
+ * @c: UBIFS file-system description object
+ * @buf: buffer to put padding to
+ * @pad: how many bytes to pad
+ *
+ * The flash media obliges us to write only in chunks of %c->min_io_size and
+ * when we have to write less data we add padding node to the write-buffer and
+ * pad it to the next minimal I/O unit's boundary. Padding nodes help when the
+ * media is being scanned. If the amount of wasted space is not enough to fit a
+ * padding node which takes %UBIFS_PAD_NODE_SZ bytes, we write padding bytes
+ * pattern (%UBIFS_PADDING_BYTE).
+ *
+ * Padding nodes are also used to fill gaps when the "commit-in-gaps" method is
+ * used.
+ */
+void ubifs_pad(const struct ubifs_info *c, void *buf, int pad)
+{
+ uint32_t crc;
+
+ ubifs_assert(pad >= 0 && !(pad & 7));
+
+ if (pad >= UBIFS_PAD_NODE_SZ) {
+ struct ubifs_ch *ch = buf;
+ struct ubifs_pad_node *pad_node = buf;
+
+ ch->magic = cpu_to_le32(UBIFS_NODE_MAGIC);
+ ch->node_type = UBIFS_PAD_NODE;
+ ch->group_type = UBIFS_NO_NODE_GROUP;
+ ch->padding[0] = ch->padding[1] = 0;
+ ch->sqnum = 0;
+ ch->len = cpu_to_le32(UBIFS_PAD_NODE_SZ);
+ pad -= UBIFS_PAD_NODE_SZ;
+ pad_node->pad_len = cpu_to_le32(pad);
+ crc = crc32(UBIFS_CRC32_INIT, buf + 8, UBIFS_PAD_NODE_SZ - 8);
+ ch->crc = cpu_to_le32(crc);
+ memset(buf + UBIFS_PAD_NODE_SZ, 0, pad);
+ } else if (pad > 0)
+ /* Too little space, padding node won't fit */
+ memset(buf, UBIFS_PADDING_BYTE, pad);
+}
+
+/**
+ * next_sqnum - get next sequence number.
+ * @c: UBIFS file-system description object
+ */
+static unsigned long long next_sqnum(struct ubifs_info *c)
+{
+ unsigned long long sqnum;
+
+ spin_lock(&c->cnt_lock);
+ sqnum = ++c->max_sqnum;
+ spin_unlock(&c->cnt_lock);
+
+ if (unlikely(sqnum >= SQNUM_WARN_WATERMARK)) {
+ if (sqnum >= SQNUM_WATERMARK) {
+ ubifs_err(c, "sequence number overflow %llu, end of life",
+ sqnum);
+ ubifs_ro_mode(c, -EINVAL);
+ }
+ ubifs_warn(c, "running out of sequence numbers, end of life soon");
+ }
+
+ return sqnum;
+}
+
+/**
+ * ubifs_prepare_node - prepare node to be written to flash.
+ * @c: UBIFS file-system description object
+ * @node: the node to pad
+ * @len: node length
+ * @pad: if the buffer has to be padded
+ *
+ * This function prepares node at @node to be written to the media - it
+ * calculates node CRC, fills the common header, and adds proper padding up to
+ * the next minimum I/O unit if @pad is not zero.
+ */
+void ubifs_prepare_node(struct ubifs_info *c, void *node, int len, int pad)
+{
+ uint32_t crc;
+ struct ubifs_ch *ch = node;
+ unsigned long long sqnum = next_sqnum(c);
+
+ ubifs_assert(len >= UBIFS_CH_SZ);
+
+ ch->magic = cpu_to_le32(UBIFS_NODE_MAGIC);
+ ch->len = cpu_to_le32(len);
+ ch->group_type = UBIFS_NO_NODE_GROUP;
+ ch->sqnum = cpu_to_le64(sqnum);
+ ch->padding[0] = ch->padding[1] = 0;
+ crc = crc32(UBIFS_CRC32_INIT, node + 8, len - 8);
+ ch->crc = cpu_to_le32(crc);
+
+ if (pad) {
+ len = ALIGN(len, 8);
+ pad = ALIGN(len, c->min_io_size) - len;
+ ubifs_pad(c, node + len, pad);
+ }
+}
+
+/**
+ * ubifs_prep_grp_node - prepare node of a group to be written to flash.
+ * @c: UBIFS file-system description object
+ * @node: the node to pad
+ * @len: node length
+ * @last: indicates the last node of the group
+ *
+ * This function prepares node at @node to be written to the media - it
+ * calculates node CRC and fills the common header.
+ */
+void ubifs_prep_grp_node(struct ubifs_info *c, void *node, int len, int last)
+{
+ uint32_t crc;
+ struct ubifs_ch *ch = node;
+ unsigned long long sqnum = next_sqnum(c);
+
+ ubifs_assert(len >= UBIFS_CH_SZ);
+
+ ch->magic = cpu_to_le32(UBIFS_NODE_MAGIC);
+ ch->len = cpu_to_le32(len);
+ if (last)
+ ch->group_type = UBIFS_LAST_OF_NODE_GROUP;
+ else
+ ch->group_type = UBIFS_IN_NODE_GROUP;
+ ch->sqnum = cpu_to_le64(sqnum);
+ ch->padding[0] = ch->padding[1] = 0;
+ crc = crc32(UBIFS_CRC32_INIT, node + 8, len - 8);
+ ch->crc = cpu_to_le32(crc);
+}
+
+#ifndef __UBOOT__
+/**
+ * wbuf_timer_callback - write-buffer timer callback function.
+ * @timer: timer data (write-buffer descriptor)
+ *
+ * This function is called when the write-buffer timer expires.
+ */
+static enum hrtimer_restart wbuf_timer_callback_nolock(struct hrtimer *timer)
+{
+ struct ubifs_wbuf *wbuf = container_of(timer, struct ubifs_wbuf, timer);
+
+ dbg_io("jhead %s", dbg_jhead(wbuf->jhead));
+ wbuf->need_sync = 1;
+ wbuf->c->need_wbuf_sync = 1;
+ ubifs_wake_up_bgt(wbuf->c);
+ return HRTIMER_NORESTART;
+}
+
+/**
+ * new_wbuf_timer - start new write-buffer timer.
+ * @wbuf: write-buffer descriptor
+ */
+static void new_wbuf_timer_nolock(struct ubifs_wbuf *wbuf)
+{
+ ubifs_assert(!hrtimer_active(&wbuf->timer));
+
+ if (wbuf->no_timer)
+ return;
+ dbg_io("set timer for jhead %s, %llu-%llu millisecs",
+ dbg_jhead(wbuf->jhead),
+ div_u64(ktime_to_ns(wbuf->softlimit), USEC_PER_SEC),
+ div_u64(ktime_to_ns(wbuf->softlimit) + wbuf->delta,
+ USEC_PER_SEC));
+ hrtimer_start_range_ns(&wbuf->timer, wbuf->softlimit, wbuf->delta,
+ HRTIMER_MODE_REL);
+}
+#endif
+
+/**
+ * cancel_wbuf_timer - cancel write-buffer timer.
+ * @wbuf: write-buffer descriptor
+ */
+static void cancel_wbuf_timer_nolock(struct ubifs_wbuf *wbuf)
+{
+ if (wbuf->no_timer)
+ return;
+ wbuf->need_sync = 0;
+#ifndef __UBOOT__
+ hrtimer_cancel(&wbuf->timer);
+#endif
+}
+
+/**
+ * ubifs_wbuf_sync_nolock - synchronize write-buffer.
+ * @wbuf: write-buffer to synchronize
+ *
+ * This function synchronizes write-buffer @buf and returns zero in case of
+ * success or a negative error code in case of failure.
+ *
+ * Note, although write-buffers are of @c->max_write_size, this function does
+ * not necessarily writes all @c->max_write_size bytes to the flash. Instead,
+ * if the write-buffer is only partially filled with data, only the used part
+ * of the write-buffer (aligned on @c->min_io_size boundary) is synchronized.
+ * This way we waste less space.
+ */
+int ubifs_wbuf_sync_nolock(struct ubifs_wbuf *wbuf)
+{
+ struct ubifs_info *c = wbuf->c;
+ int err, dirt, sync_len;
+
+ cancel_wbuf_timer_nolock(wbuf);
+ if (!wbuf->used || wbuf->lnum == -1)
+ /* Write-buffer is empty or not seeked */
+ return 0;
+
+ dbg_io("LEB %d:%d, %d bytes, jhead %s",
+ wbuf->lnum, wbuf->offs, wbuf->used, dbg_jhead(wbuf->jhead));
+ ubifs_assert(!(wbuf->avail & 7));
+ ubifs_assert(wbuf->offs + wbuf->size <= c->leb_size);
+ ubifs_assert(wbuf->size >= c->min_io_size);
+ ubifs_assert(wbuf->size <= c->max_write_size);
+ ubifs_assert(wbuf->size % c->min_io_size == 0);
+ ubifs_assert(!c->ro_media && !c->ro_mount);
+ if (c->leb_size - wbuf->offs >= c->max_write_size)
+ ubifs_assert(!((wbuf->offs + wbuf->size) % c->max_write_size));
+
+ if (c->ro_error)
+ return -EROFS;
+
+ /*
+ * Do not write whole write buffer but write only the minimum necessary
+ * amount of min. I/O units.
+ */
+ sync_len = ALIGN(wbuf->used, c->min_io_size);
+ dirt = sync_len - wbuf->used;
+ if (dirt)
+ ubifs_pad(c, wbuf->buf + wbuf->used, dirt);
+ err = ubifs_leb_write(c, wbuf->lnum, wbuf->buf, wbuf->offs, sync_len);
+ if (err)
+ return err;
+
+ spin_lock(&wbuf->lock);
+ wbuf->offs += sync_len;
+ /*
+ * Now @wbuf->offs is not necessarily aligned to @c->max_write_size.
+ * But our goal is to optimize writes and make sure we write in
+ * @c->max_write_size chunks and to @c->max_write_size-aligned offset.
+ * Thus, if @wbuf->offs is not aligned to @c->max_write_size now, make
+ * sure that @wbuf->offs + @wbuf->size is aligned to
+ * @c->max_write_size. This way we make sure that after next
+ * write-buffer flush we are again at the optimal offset (aligned to
+ * @c->max_write_size).
+ */
+ if (c->leb_size - wbuf->offs < c->max_write_size)
+ wbuf->size = c->leb_size - wbuf->offs;
+ else if (wbuf->offs & (c->max_write_size - 1))
+ wbuf->size = ALIGN(wbuf->offs, c->max_write_size) - wbuf->offs;
+ else
+ wbuf->size = c->max_write_size;
+ wbuf->avail = wbuf->size;
+ wbuf->used = 0;
+ wbuf->next_ino = 0;
+ spin_unlock(&wbuf->lock);
+
+ if (wbuf->sync_callback)
+ err = wbuf->sync_callback(c, wbuf->lnum,
+ c->leb_size - wbuf->offs, dirt);
+ return err;
+}
+
+/**
+ * ubifs_wbuf_seek_nolock - seek write-buffer.
+ * @wbuf: write-buffer
+ * @lnum: logical eraseblock number to seek to
+ * @offs: logical eraseblock offset to seek to
+ *
+ * This function targets the write-buffer to logical eraseblock @lnum:@offs.
+ * The write-buffer has to be empty. Returns zero in case of success and a
+ * negative error code in case of failure.
+ */
+int ubifs_wbuf_seek_nolock(struct ubifs_wbuf *wbuf, int lnum, int offs)
+{
+ const struct ubifs_info *c = wbuf->c;
+
+ dbg_io("LEB %d:%d, jhead %s", lnum, offs, dbg_jhead(wbuf->jhead));
+ ubifs_assert(lnum >= 0 && lnum < c->leb_cnt);
+ ubifs_assert(offs >= 0 && offs <= c->leb_size);
+ ubifs_assert(offs % c->min_io_size == 0 && !(offs & 7));
+ ubifs_assert(lnum != wbuf->lnum);
+ ubifs_assert(wbuf->used == 0);
+
+ spin_lock(&wbuf->lock);
+ wbuf->lnum = lnum;
+ wbuf->offs = offs;
+ if (c->leb_size - wbuf->offs < c->max_write_size)
+ wbuf->size = c->leb_size - wbuf->offs;
+ else if (wbuf->offs & (c->max_write_size - 1))
+ wbuf->size = ALIGN(wbuf->offs, c->max_write_size) - wbuf->offs;
+ else
+ wbuf->size = c->max_write_size;
+ wbuf->avail = wbuf->size;
+ wbuf->used = 0;
+ spin_unlock(&wbuf->lock);
+
+ return 0;
+}
+
+#ifndef __UBOOT__
+/**
+ * ubifs_bg_wbufs_sync - synchronize write-buffers.
+ * @c: UBIFS file-system description object
+ *
+ * This function is called by background thread to synchronize write-buffers.
+ * Returns zero in case of success and a negative error code in case of
+ * failure.
+ */
+int ubifs_bg_wbufs_sync(struct ubifs_info *c)
+{
+ int err, i;
+
+ ubifs_assert(!c->ro_media && !c->ro_mount);
+ if (!c->need_wbuf_sync)
+ return 0;
+ c->need_wbuf_sync = 0;
+
+ if (c->ro_error) {
+ err = -EROFS;
+ goto out_timers;
+ }
+
+ dbg_io("synchronize");
+ for (i = 0; i < c->jhead_cnt; i++) {
+ struct ubifs_wbuf *wbuf = &c->jheads[i].wbuf;
+
+ cond_resched();
+
+ /*
+ * If the mutex is locked then wbuf is being changed, so
+ * synchronization is not necessary.
+ */
+ if (mutex_is_locked(&wbuf->io_mutex))
+ continue;
+
+ mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead);
+ if (!wbuf->need_sync) {
+ mutex_unlock(&wbuf->io_mutex);
+ continue;
+ }
+
+ err = ubifs_wbuf_sync_nolock(wbuf);
+ mutex_unlock(&wbuf->io_mutex);
+ if (err) {
+ ubifs_err(c, "cannot sync write-buffer, error %d", err);
+ ubifs_ro_mode(c, err);
+ goto out_timers;
+ }
+ }
+
+ return 0;
+
+out_timers:
+ /* Cancel all timers to prevent repeated errors */
+ for (i = 0; i < c->jhead_cnt; i++) {
+ struct ubifs_wbuf *wbuf = &c->jheads[i].wbuf;
+
+ mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead);
+ cancel_wbuf_timer_nolock(wbuf);
+ mutex_unlock(&wbuf->io_mutex);
+ }
+ return err;
+}
+
+/**
+ * ubifs_wbuf_write_nolock - write data to flash via write-buffer.
+ * @wbuf: write-buffer
+ * @buf: node to write
+ * @len: node length
+ *
+ * This function writes data to flash via write-buffer @wbuf. This means that
+ * the last piece of the node won't reach the flash media immediately if it
+ * does not take whole max. write unit (@c->max_write_size). Instead, the node
+ * will sit in RAM until the write-buffer is synchronized (e.g., by timer, or
+ * because more data are appended to the write-buffer).
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure. If the node cannot be written because there is no more
+ * space in this logical eraseblock, %-ENOSPC is returned.
+ */
+int ubifs_wbuf_write_nolock(struct ubifs_wbuf *wbuf, void *buf, int len)
+{
+ struct ubifs_info *c = wbuf->c;
+ int err, written, n, aligned_len = ALIGN(len, 8);
+
+ dbg_io("%d bytes (%s) to jhead %s wbuf at LEB %d:%d", len,
+ dbg_ntype(((struct ubifs_ch *)buf)->node_type),
+ dbg_jhead(wbuf->jhead), wbuf->lnum, wbuf->offs + wbuf->used);
+ ubifs_assert(len > 0 && wbuf->lnum >= 0 && wbuf->lnum < c->leb_cnt);
+ ubifs_assert(wbuf->offs >= 0 && wbuf->offs % c->min_io_size == 0);
+ ubifs_assert(!(wbuf->offs & 7) && wbuf->offs <= c->leb_size);
+ ubifs_assert(wbuf->avail > 0 && wbuf->avail <= wbuf->size);
+ ubifs_assert(wbuf->size >= c->min_io_size);
+ ubifs_assert(wbuf->size <= c->max_write_size);
+ ubifs_assert(wbuf->size % c->min_io_size == 0);
+ ubifs_assert(mutex_is_locked(&wbuf->io_mutex));
+ ubifs_assert(!c->ro_media && !c->ro_mount);
+ ubifs_assert(!c->space_fixup);
+ if (c->leb_size - wbuf->offs >= c->max_write_size)
+ ubifs_assert(!((wbuf->offs + wbuf->size) % c->max_write_size));
+
+ if (c->leb_size - wbuf->offs - wbuf->used < aligned_len) {
+ err = -ENOSPC;
+ goto out;
+ }
+
+ cancel_wbuf_timer_nolock(wbuf);
+
+ if (c->ro_error)
+ return -EROFS;
+
+ if (aligned_len <= wbuf->avail) {
+ /*
+ * The node is not very large and fits entirely within
+ * write-buffer.
+ */
+ memcpy(wbuf->buf + wbuf->used, buf, len);
+
+ if (aligned_len == wbuf->avail) {
+ dbg_io("flush jhead %s wbuf to LEB %d:%d",
+ dbg_jhead(wbuf->jhead), wbuf->lnum, wbuf->offs);
+ err = ubifs_leb_write(c, wbuf->lnum, wbuf->buf,
+ wbuf->offs, wbuf->size);
+ if (err)
+ goto out;
+
+ spin_lock(&wbuf->lock);
+ wbuf->offs += wbuf->size;
+ if (c->leb_size - wbuf->offs >= c->max_write_size)
+ wbuf->size = c->max_write_size;
+ else
+ wbuf->size = c->leb_size - wbuf->offs;
+ wbuf->avail = wbuf->size;
+ wbuf->used = 0;
+ wbuf->next_ino = 0;
+ spin_unlock(&wbuf->lock);
+ } else {
+ spin_lock(&wbuf->lock);
+ wbuf->avail -= aligned_len;
+ wbuf->used += aligned_len;
+ spin_unlock(&wbuf->lock);
+ }
+
+ goto exit;
+ }
+
+ written = 0;
+
+ if (wbuf->used) {
+ /*
+ * The node is large enough and does not fit entirely within
+ * current available space. We have to fill and flush
+ * write-buffer and switch to the next max. write unit.
+ */
+ dbg_io("flush jhead %s wbuf to LEB %d:%d",
+ dbg_jhead(wbuf->jhead), wbuf->lnum, wbuf->offs);
+ memcpy(wbuf->buf + wbuf->used, buf, wbuf->avail);
+ err = ubifs_leb_write(c, wbuf->lnum, wbuf->buf, wbuf->offs,
+ wbuf->size);
+ if (err)
+ goto out;
+
+ wbuf->offs += wbuf->size;
+ len -= wbuf->avail;
+ aligned_len -= wbuf->avail;
+ written += wbuf->avail;
+ } else if (wbuf->offs & (c->max_write_size - 1)) {
+ /*
+ * The write-buffer offset is not aligned to
+ * @c->max_write_size and @wbuf->size is less than
+ * @c->max_write_size. Write @wbuf->size bytes to make sure the
+ * following writes are done in optimal @c->max_write_size
+ * chunks.
+ */
+ dbg_io("write %d bytes to LEB %d:%d",
+ wbuf->size, wbuf->lnum, wbuf->offs);
+ err = ubifs_leb_write(c, wbuf->lnum, buf, wbuf->offs,
+ wbuf->size);
+ if (err)
+ goto out;
+
+ wbuf->offs += wbuf->size;
+ len -= wbuf->size;
+ aligned_len -= wbuf->size;
+ written += wbuf->size;
+ }
+
+ /*
+ * The remaining data may take more whole max. write units, so write the
+ * remains multiple to max. write unit size directly to the flash media.
+ * We align node length to 8-byte boundary because we anyway flash wbuf
+ * if the remaining space is less than 8 bytes.
+ */
+ n = aligned_len >> c->max_write_shift;
+ if (n) {
+ n <<= c->max_write_shift;
+ dbg_io("write %d bytes to LEB %d:%d", n, wbuf->lnum,
+ wbuf->offs);
+ err = ubifs_leb_write(c, wbuf->lnum, buf + written,
+ wbuf->offs, n);
+ if (err)
+ goto out;
+ wbuf->offs += n;
+ aligned_len -= n;
+ len -= n;
+ written += n;
+ }
+
+ spin_lock(&wbuf->lock);
+ if (aligned_len)
+ /*
+ * And now we have what's left and what does not take whole
+ * max. write unit, so write it to the write-buffer and we are
+ * done.
+ */
+ memcpy(wbuf->buf, buf + written, len);
+
+ if (c->leb_size - wbuf->offs >= c->max_write_size)
+ wbuf->size = c->max_write_size;
+ else
+ wbuf->size = c->leb_size - wbuf->offs;
+ wbuf->avail = wbuf->size - aligned_len;
+ wbuf->used = aligned_len;
+ wbuf->next_ino = 0;
+ spin_unlock(&wbuf->lock);
+
+exit:
+ if (wbuf->sync_callback) {
+ int free = c->leb_size - wbuf->offs - wbuf->used;
+
+ err = wbuf->sync_callback(c, wbuf->lnum, free, 0);
+ if (err)
+ goto out;
+ }
+
+ if (wbuf->used)
+ new_wbuf_timer_nolock(wbuf);
+
+ return 0;
+
+out:
+ ubifs_err(c, "cannot write %d bytes to LEB %d:%d, error %d",
+ len, wbuf->lnum, wbuf->offs, err);
+ ubifs_dump_node(c, buf);
+ dump_stack();
+ ubifs_dump_leb(c, wbuf->lnum);
+ return err;
+}
+
+/**
+ * ubifs_write_node - write node to the media.
+ * @c: UBIFS file-system description object
+ * @buf: the node to write
+ * @len: node length
+ * @lnum: logical eraseblock number
+ * @offs: offset within the logical eraseblock
+ *
+ * This function automatically fills node magic number, assigns sequence
+ * number, and calculates node CRC checksum. The length of the @buf buffer has
+ * to be aligned to the minimal I/O unit size. This function automatically
+ * appends padding node and padding bytes if needed. Returns zero in case of
+ * success and a negative error code in case of failure.
+ */
+int ubifs_write_node(struct ubifs_info *c, void *buf, int len, int lnum,
+ int offs)
+{
+ int err, buf_len = ALIGN(len, c->min_io_size);
+
+ dbg_io("LEB %d:%d, %s, length %d (aligned %d)",
+ lnum, offs, dbg_ntype(((struct ubifs_ch *)buf)->node_type), len,
+ buf_len);
+ ubifs_assert(lnum >= 0 && lnum < c->leb_cnt && offs >= 0);
+ ubifs_assert(offs % c->min_io_size == 0 && offs < c->leb_size);
+ ubifs_assert(!c->ro_media && !c->ro_mount);
+ ubifs_assert(!c->space_fixup);
+
+ if (c->ro_error)
+ return -EROFS;
+
+ ubifs_prepare_node(c, buf, len, 1);
+ err = ubifs_leb_write(c, lnum, buf, offs, buf_len);
+ if (err)
+ ubifs_dump_node(c, buf);
+
+ return err;
+}
+#endif
+
+/**
+ * ubifs_read_node_wbuf - read node from the media or write-buffer.
+ * @wbuf: wbuf to check for un-written data
+ * @buf: buffer to read to
+ * @type: node type
+ * @len: node length
+ * @lnum: logical eraseblock number
+ * @offs: offset within the logical eraseblock
+ *
+ * This function reads a node of known type and length, checks it and stores
+ * in @buf. If the node partially or fully sits in the write-buffer, this
+ * function takes data from the buffer, otherwise it reads the flash media.
+ * Returns zero in case of success, %-EUCLEAN if CRC mismatched and a negative
+ * error code in case of failure.
+ */
+int ubifs_read_node_wbuf(struct ubifs_wbuf *wbuf, void *buf, int type, int len,
+ int lnum, int offs)
+{
+ const struct ubifs_info *c = wbuf->c;
+ int err, rlen, overlap;
+ struct ubifs_ch *ch = buf;
+
+ dbg_io("LEB %d:%d, %s, length %d, jhead %s", lnum, offs,
+ dbg_ntype(type), len, dbg_jhead(wbuf->jhead));
+ ubifs_assert(wbuf && lnum >= 0 && lnum < c->leb_cnt && offs >= 0);
+ ubifs_assert(!(offs & 7) && offs < c->leb_size);
+ ubifs_assert(type >= 0 && type < UBIFS_NODE_TYPES_CNT);
+
+ spin_lock(&wbuf->lock);
+ overlap = (lnum == wbuf->lnum && offs + len > wbuf->offs);
+ if (!overlap) {
+ /* We may safely unlock the write-buffer and read the data */
+ spin_unlock(&wbuf->lock);
+ return ubifs_read_node(c, buf, type, len, lnum, offs);
+ }
+
+ /* Don't read under wbuf */
+ rlen = wbuf->offs - offs;
+ if (rlen < 0)
+ rlen = 0;
+
+ /* Copy the rest from the write-buffer */
+ memcpy(buf + rlen, wbuf->buf + offs + rlen - wbuf->offs, len - rlen);
+ spin_unlock(&wbuf->lock);
+
+ if (rlen > 0) {
+ /* Read everything that goes before write-buffer */
+ err = ubifs_leb_read(c, lnum, buf, offs, rlen, 0);
+ if (err && err != -EBADMSG)
+ return err;
+ }
+
+ if (type != ch->node_type) {
+ ubifs_err(c, "bad node type (%d but expected %d)",
+ ch->node_type, type);
+ goto out;
+ }
+
+ err = ubifs_check_node(c, buf, lnum, offs, 0, 0);
+ if (err) {
+ ubifs_err(c, "expected node type %d", type);
+ return err;
+ }
+
+ rlen = le32_to_cpu(ch->len);
+ if (rlen != len) {
+ ubifs_err(c, "bad node length %d, expected %d", rlen, len);
+ goto out;
+ }
+
+ return 0;
+
+out:
+ ubifs_err(c, "bad node at LEB %d:%d", lnum, offs);
+ ubifs_dump_node(c, buf);
+ dump_stack();
+ return -EINVAL;
+}
+
+/**
+ * ubifs_read_node - read node.
+ * @c: UBIFS file-system description object
+ * @buf: buffer to read to
+ * @type: node type
+ * @len: node length (not aligned)
+ * @lnum: logical eraseblock number
+ * @offs: offset within the logical eraseblock
+ *
+ * This function reads a node of known type and and length, checks it and
+ * stores in @buf. Returns zero in case of success, %-EUCLEAN if CRC mismatched
+ * and a negative error code in case of failure.
+ */
+int ubifs_read_node(const struct ubifs_info *c, void *buf, int type, int len,
+ int lnum, int offs)
+{
+ int err, l;
+ struct ubifs_ch *ch = buf;
+
+ dbg_io("LEB %d:%d, %s, length %d", lnum, offs, dbg_ntype(type), len);
+ ubifs_assert(lnum >= 0 && lnum < c->leb_cnt && offs >= 0);
+ ubifs_assert(len >= UBIFS_CH_SZ && offs + len <= c->leb_size);
+ ubifs_assert(!(offs & 7) && offs < c->leb_size);
+ ubifs_assert(type >= 0 && type < UBIFS_NODE_TYPES_CNT);
+
+ err = ubifs_leb_read(c, lnum, buf, offs, len, 0);
+ if (err && err != -EBADMSG)
+ return err;
+
+ if (type != ch->node_type) {
+ ubifs_errc(c, "bad node type (%d but expected %d)",
+ ch->node_type, type);
+ goto out;
+ }
+
+ err = ubifs_check_node(c, buf, lnum, offs, 0, 0);
+ if (err) {
+ ubifs_errc(c, "expected node type %d", type);
+ return err;
+ }
+
+ l = le32_to_cpu(ch->len);
+ if (l != len) {
+ ubifs_errc(c, "bad node length %d, expected %d", l, len);
+ goto out;
+ }
+
+ return 0;
+
+out:
+ ubifs_errc(c, "bad node at LEB %d:%d, LEB mapping status %d", lnum,
+ offs, ubi_is_mapped(c->ubi, lnum));
+ if (!c->probing) {
+ ubifs_dump_node(c, buf);
+ dump_stack();
+ }
+ return -EINVAL;
+}
+
+/**
+ * ubifs_wbuf_init - initialize write-buffer.
+ * @c: UBIFS file-system description object
+ * @wbuf: write-buffer to initialize
+ *
+ * This function initializes write-buffer. Returns zero in case of success
+ * %-ENOMEM in case of failure.
+ */
+int ubifs_wbuf_init(struct ubifs_info *c, struct ubifs_wbuf *wbuf)
+{
+ size_t size;
+
+ wbuf->buf = kmalloc(c->max_write_size, GFP_KERNEL);
+ if (!wbuf->buf)
+ return -ENOMEM;
+
+ size = (c->max_write_size / UBIFS_CH_SZ + 1) * sizeof(ino_t);
+ wbuf->inodes = kmalloc(size, GFP_KERNEL);
+ if (!wbuf->inodes) {
+ kfree(wbuf->buf);
+ wbuf->buf = NULL;
+ return -ENOMEM;
+ }
+
+ wbuf->used = 0;
+ wbuf->lnum = wbuf->offs = -1;
+ /*
+ * If the LEB starts at the max. write size aligned address, then
+ * write-buffer size has to be set to @c->max_write_size. Otherwise,
+ * set it to something smaller so that it ends at the closest max.
+ * write size boundary.
+ */
+ size = c->max_write_size - (c->leb_start % c->max_write_size);
+ wbuf->avail = wbuf->size = size;
+ wbuf->sync_callback = NULL;
+ mutex_init(&wbuf->io_mutex);
+ spin_lock_init(&wbuf->lock);
+ wbuf->c = c;
+ wbuf->next_ino = 0;
+
+#ifndef __UBOOT__
+ hrtimer_init(&wbuf->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ wbuf->timer.function = wbuf_timer_callback_nolock;
+ wbuf->softlimit = ktime_set(WBUF_TIMEOUT_SOFTLIMIT, 0);
+ wbuf->delta = WBUF_TIMEOUT_HARDLIMIT - WBUF_TIMEOUT_SOFTLIMIT;
+ wbuf->delta *= 1000000000ULL;
+ ubifs_assert(wbuf->delta <= ULONG_MAX);
+#endif
+ return 0;
+}
+
+/**
+ * ubifs_wbuf_add_ino_nolock - add an inode number into the wbuf inode array.
+ * @wbuf: the write-buffer where to add
+ * @inum: the inode number
+ *
+ * This function adds an inode number to the inode array of the write-buffer.
+ */
+void ubifs_wbuf_add_ino_nolock(struct ubifs_wbuf *wbuf, ino_t inum)
+{
+ if (!wbuf->buf)
+ /* NOR flash or something similar */
+ return;
+
+ spin_lock(&wbuf->lock);
+ if (wbuf->used)
+ wbuf->inodes[wbuf->next_ino++] = inum;
+ spin_unlock(&wbuf->lock);
+}
+
+/**
+ * wbuf_has_ino - returns if the wbuf contains data from the inode.
+ * @wbuf: the write-buffer
+ * @inum: the inode number
+ *
+ * This function returns with %1 if the write-buffer contains some data from the
+ * given inode otherwise it returns with %0.
+ */
+static int wbuf_has_ino(struct ubifs_wbuf *wbuf, ino_t inum)
+{
+ int i, ret = 0;
+
+ spin_lock(&wbuf->lock);
+ for (i = 0; i < wbuf->next_ino; i++)
+ if (inum == wbuf->inodes[i]) {
+ ret = 1;
+ break;
+ }
+ spin_unlock(&wbuf->lock);
+
+ return ret;
+}
+
+/**
+ * ubifs_sync_wbufs_by_inode - synchronize write-buffers for an inode.
+ * @c: UBIFS file-system description object
+ * @inode: inode to synchronize
+ *
+ * This function synchronizes write-buffers which contain nodes belonging to
+ * @inode. Returns zero in case of success and a negative error code in case of
+ * failure.
+ */
+int ubifs_sync_wbufs_by_inode(struct ubifs_info *c, struct inode *inode)
+{
+ int i, err = 0;
+
+ for (i = 0; i < c->jhead_cnt; i++) {
+ struct ubifs_wbuf *wbuf = &c->jheads[i].wbuf;
+
+ if (i == GCHD)
+ /*
+ * GC head is special, do not look at it. Even if the
+ * head contains something related to this inode, it is
+ * a _copy_ of corresponding on-flash node which sits
+ * somewhere else.
+ */
+ continue;
+
+ if (!wbuf_has_ino(wbuf, inode->i_ino))
+ continue;
+
+ mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead);
+ if (wbuf_has_ino(wbuf, inode->i_ino))
+ err = ubifs_wbuf_sync_nolock(wbuf);
+ mutex_unlock(&wbuf->io_mutex);
+
+ if (err) {
+ ubifs_ro_mode(c, err);
+ return err;
+ }
+ }
+ return 0;
+}
diff --git a/roms/u-boot/fs/ubifs/key.h b/roms/u-boot/fs/ubifs/key.h
new file mode 100644
index 000000000..70c2faf33
--- /dev/null
+++ b/roms/u-boot/fs/ubifs/key.h
@@ -0,0 +1,536 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ *
+ * Authors: Artem Bityutskiy (Битюцкий Артём)
+ * Adrian Hunter
+ */
+
+/*
+ * This header contains various key-related definitions and helper function.
+ * UBIFS allows several key schemes, so we access key fields only via these
+ * helpers. At the moment only one key scheme is supported.
+ *
+ * Simple key scheme
+ * ~~~~~~~~~~~~~~~~~
+ *
+ * Keys are 64-bits long. First 32-bits are inode number (parent inode number
+ * in case of direntry key). Next 3 bits are node type. The last 29 bits are
+ * 4KiB offset in case of inode node, and direntry hash in case of a direntry
+ * node. We use "r5" hash borrowed from reiserfs.
+ */
+
+#ifndef __UBIFS_KEY_H__
+#define __UBIFS_KEY_H__
+
+/**
+ * key_mask_hash - mask a valid hash value.
+ * @val: value to be masked
+ *
+ * We use hash values as offset in directories, so values %0 and %1 are
+ * reserved for "." and "..". %2 is reserved for "end of readdir" marker. This
+ * function makes sure the reserved values are not used.
+ */
+static inline uint32_t key_mask_hash(uint32_t hash)
+{
+ hash &= UBIFS_S_KEY_HASH_MASK;
+ if (unlikely(hash <= 2))
+ hash += 3;
+ return hash;
+}
+
+/**
+ * key_r5_hash - R5 hash function (borrowed from reiserfs).
+ * @s: direntry name
+ * @len: name length
+ */
+static inline uint32_t key_r5_hash(const char *s, int len)
+{
+ uint32_t a = 0;
+ const signed char *str = (const signed char *)s;
+
+ while (*str) {
+ a += *str << 4;
+ a += *str >> 4;
+ a *= 11;
+ str++;
+ }
+
+ return key_mask_hash(a);
+}
+
+/**
+ * key_test_hash - testing hash function.
+ * @str: direntry name
+ * @len: name length
+ */
+static inline uint32_t key_test_hash(const char *str, int len)
+{
+ uint32_t a = 0;
+
+ len = min_t(uint32_t, len, 4);
+ memcpy(&a, str, len);
+ return key_mask_hash(a);
+}
+
+/**
+ * ino_key_init - initialize inode key.
+ * @c: UBIFS file-system description object
+ * @key: key to initialize
+ * @inum: inode number
+ */
+static inline void ino_key_init(const struct ubifs_info *c,
+ union ubifs_key *key, ino_t inum)
+{
+ key->u32[0] = inum;
+ key->u32[1] = UBIFS_INO_KEY << UBIFS_S_KEY_BLOCK_BITS;
+}
+
+/**
+ * ino_key_init_flash - initialize on-flash inode key.
+ * @c: UBIFS file-system description object
+ * @k: key to initialize
+ * @inum: inode number
+ */
+static inline void ino_key_init_flash(const struct ubifs_info *c, void *k,
+ ino_t inum)
+{
+ union ubifs_key *key = k;
+
+ key->j32[0] = cpu_to_le32(inum);
+ key->j32[1] = cpu_to_le32(UBIFS_INO_KEY << UBIFS_S_KEY_BLOCK_BITS);
+ memset(k + 8, 0, UBIFS_MAX_KEY_LEN - 8);
+}
+
+/**
+ * lowest_ino_key - get the lowest possible inode key.
+ * @c: UBIFS file-system description object
+ * @key: key to initialize
+ * @inum: inode number
+ */
+static inline void lowest_ino_key(const struct ubifs_info *c,
+ union ubifs_key *key, ino_t inum)
+{
+ key->u32[0] = inum;
+ key->u32[1] = 0;
+}
+
+/**
+ * highest_ino_key - get the highest possible inode key.
+ * @c: UBIFS file-system description object
+ * @key: key to initialize
+ * @inum: inode number
+ */
+static inline void highest_ino_key(const struct ubifs_info *c,
+ union ubifs_key *key, ino_t inum)
+{
+ key->u32[0] = inum;
+ key->u32[1] = 0xffffffff;
+}
+
+/**
+ * dent_key_init - initialize directory entry key.
+ * @c: UBIFS file-system description object
+ * @key: key to initialize
+ * @inum: parent inode number
+ * @nm: direntry name and length
+ */
+static inline void dent_key_init(const struct ubifs_info *c,
+ union ubifs_key *key, ino_t inum,
+ const struct qstr *nm)
+{
+ uint32_t hash = c->key_hash(nm->name, nm->len);
+
+ ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK));
+ key->u32[0] = inum;
+ key->u32[1] = hash | (UBIFS_DENT_KEY << UBIFS_S_KEY_HASH_BITS);
+}
+
+/**
+ * dent_key_init_hash - initialize directory entry key without re-calculating
+ * hash function.
+ * @c: UBIFS file-system description object
+ * @key: key to initialize
+ * @inum: parent inode number
+ * @hash: direntry name hash
+ */
+static inline void dent_key_init_hash(const struct ubifs_info *c,
+ union ubifs_key *key, ino_t inum,
+ uint32_t hash)
+{
+ ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK));
+ key->u32[0] = inum;
+ key->u32[1] = hash | (UBIFS_DENT_KEY << UBIFS_S_KEY_HASH_BITS);
+}
+
+/**
+ * dent_key_init_flash - initialize on-flash directory entry key.
+ * @c: UBIFS file-system description object
+ * @k: key to initialize
+ * @inum: parent inode number
+ * @nm: direntry name and length
+ */
+static inline void dent_key_init_flash(const struct ubifs_info *c, void *k,
+ ino_t inum, const struct qstr *nm)
+{
+ union ubifs_key *key = k;
+ uint32_t hash = c->key_hash(nm->name, nm->len);
+
+ ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK));
+ key->j32[0] = cpu_to_le32(inum);
+ key->j32[1] = cpu_to_le32(hash |
+ (UBIFS_DENT_KEY << UBIFS_S_KEY_HASH_BITS));
+ memset(k + 8, 0, UBIFS_MAX_KEY_LEN - 8);
+}
+
+/**
+ * lowest_dent_key - get the lowest possible directory entry key.
+ * @c: UBIFS file-system description object
+ * @key: where to store the lowest key
+ * @inum: parent inode number
+ */
+static inline void lowest_dent_key(const struct ubifs_info *c,
+ union ubifs_key *key, ino_t inum)
+{
+ key->u32[0] = inum;
+ key->u32[1] = UBIFS_DENT_KEY << UBIFS_S_KEY_HASH_BITS;
+}
+
+/**
+ * xent_key_init - initialize extended attribute entry key.
+ * @c: UBIFS file-system description object
+ * @key: key to initialize
+ * @inum: host inode number
+ * @nm: extended attribute entry name and length
+ */
+static inline void xent_key_init(const struct ubifs_info *c,
+ union ubifs_key *key, ino_t inum,
+ const struct qstr *nm)
+{
+ uint32_t hash = c->key_hash(nm->name, nm->len);
+
+ ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK));
+ key->u32[0] = inum;
+ key->u32[1] = hash | (UBIFS_XENT_KEY << UBIFS_S_KEY_HASH_BITS);
+}
+
+/**
+ * xent_key_init_flash - initialize on-flash extended attribute entry key.
+ * @c: UBIFS file-system description object
+ * @k: key to initialize
+ * @inum: host inode number
+ * @nm: extended attribute entry name and length
+ */
+static inline void xent_key_init_flash(const struct ubifs_info *c, void *k,
+ ino_t inum, const struct qstr *nm)
+{
+ union ubifs_key *key = k;
+ uint32_t hash = c->key_hash(nm->name, nm->len);
+
+ ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK));
+ key->j32[0] = cpu_to_le32(inum);
+ key->j32[1] = cpu_to_le32(hash |
+ (UBIFS_XENT_KEY << UBIFS_S_KEY_HASH_BITS));
+ memset(k + 8, 0, UBIFS_MAX_KEY_LEN - 8);
+}
+
+/**
+ * lowest_xent_key - get the lowest possible extended attribute entry key.
+ * @c: UBIFS file-system description object
+ * @key: where to store the lowest key
+ * @inum: host inode number
+ */
+static inline void lowest_xent_key(const struct ubifs_info *c,
+ union ubifs_key *key, ino_t inum)
+{
+ key->u32[0] = inum;
+ key->u32[1] = UBIFS_XENT_KEY << UBIFS_S_KEY_HASH_BITS;
+}
+
+/**
+ * data_key_init - initialize data key.
+ * @c: UBIFS file-system description object
+ * @key: key to initialize
+ * @inum: inode number
+ * @block: block number
+ */
+static inline void data_key_init(const struct ubifs_info *c,
+ union ubifs_key *key, ino_t inum,
+ unsigned int block)
+{
+ ubifs_assert(!(block & ~UBIFS_S_KEY_BLOCK_MASK));
+ key->u32[0] = inum;
+ key->u32[1] = block | (UBIFS_DATA_KEY << UBIFS_S_KEY_BLOCK_BITS);
+}
+
+/**
+ * highest_data_key - get the highest possible data key for an inode.
+ * @c: UBIFS file-system description object
+ * @key: key to initialize
+ * @inum: inode number
+ */
+static inline void highest_data_key(const struct ubifs_info *c,
+ union ubifs_key *key, ino_t inum)
+{
+ data_key_init(c, key, inum, UBIFS_S_KEY_BLOCK_MASK);
+}
+
+/**
+ * trun_key_init - initialize truncation node key.
+ * @c: UBIFS file-system description object
+ * @key: key to initialize
+ * @inum: inode number
+ *
+ * Note, UBIFS does not have truncation keys on the media and this function is
+ * only used for purposes of replay.
+ */
+static inline void trun_key_init(const struct ubifs_info *c,
+ union ubifs_key *key, ino_t inum)
+{
+ key->u32[0] = inum;
+ key->u32[1] = UBIFS_TRUN_KEY << UBIFS_S_KEY_BLOCK_BITS;
+}
+
+/**
+ * invalid_key_init - initialize invalid node key.
+ * @c: UBIFS file-system description object
+ * @key: key to initialize
+ *
+ * This is a helper function which marks a @key object as invalid.
+ */
+static inline void invalid_key_init(const struct ubifs_info *c,
+ union ubifs_key *key)
+{
+ key->u32[0] = 0xDEADBEAF;
+ key->u32[1] = UBIFS_INVALID_KEY;
+}
+
+/**
+ * key_type - get key type.
+ * @c: UBIFS file-system description object
+ * @key: key to get type of
+ */
+static inline int key_type(const struct ubifs_info *c,
+ const union ubifs_key *key)
+{
+ return key->u32[1] >> UBIFS_S_KEY_BLOCK_BITS;
+}
+
+/**
+ * key_type_flash - get type of a on-flash formatted key.
+ * @c: UBIFS file-system description object
+ * @k: key to get type of
+ */
+static inline int key_type_flash(const struct ubifs_info *c, const void *k)
+{
+ const union ubifs_key *key = k;
+
+ return le32_to_cpu(key->j32[1]) >> UBIFS_S_KEY_BLOCK_BITS;
+}
+
+/**
+ * key_inum - fetch inode number from key.
+ * @c: UBIFS file-system description object
+ * @k: key to fetch inode number from
+ */
+static inline ino_t key_inum(const struct ubifs_info *c, const void *k)
+{
+ const union ubifs_key *key = k;
+
+ return key->u32[0];
+}
+
+/**
+ * key_inum_flash - fetch inode number from an on-flash formatted key.
+ * @c: UBIFS file-system description object
+ * @k: key to fetch inode number from
+ */
+static inline ino_t key_inum_flash(const struct ubifs_info *c, const void *k)
+{
+ const union ubifs_key *key = k;
+
+ return le32_to_cpu(key->j32[0]);
+}
+
+/**
+ * key_hash - get directory entry hash.
+ * @c: UBIFS file-system description object
+ * @key: the key to get hash from
+ */
+static inline uint32_t key_hash(const struct ubifs_info *c,
+ const union ubifs_key *key)
+{
+ return key->u32[1] & UBIFS_S_KEY_HASH_MASK;
+}
+
+/**
+ * key_hash_flash - get directory entry hash from an on-flash formatted key.
+ * @c: UBIFS file-system description object
+ * @k: the key to get hash from
+ */
+static inline uint32_t key_hash_flash(const struct ubifs_info *c, const void *k)
+{
+ const union ubifs_key *key = k;
+
+ return le32_to_cpu(key->j32[1]) & UBIFS_S_KEY_HASH_MASK;
+}
+
+/**
+ * key_block - get data block number.
+ * @c: UBIFS file-system description object
+ * @key: the key to get the block number from
+ */
+static inline unsigned int key_block(const struct ubifs_info *c,
+ const union ubifs_key *key)
+{
+ return key->u32[1] & UBIFS_S_KEY_BLOCK_MASK;
+}
+
+/**
+ * key_block_flash - get data block number from an on-flash formatted key.
+ * @c: UBIFS file-system description object
+ * @k: the key to get the block number from
+ */
+static inline unsigned int key_block_flash(const struct ubifs_info *c,
+ const void *k)
+{
+ const union ubifs_key *key = k;
+
+ return le32_to_cpu(key->j32[1]) & UBIFS_S_KEY_BLOCK_MASK;
+}
+
+/**
+ * key_read - transform a key to in-memory format.
+ * @c: UBIFS file-system description object
+ * @from: the key to transform
+ * @to: the key to store the result
+ */
+static inline void key_read(const struct ubifs_info *c, const void *from,
+ union ubifs_key *to)
+{
+ const union ubifs_key *f = from;
+
+ to->u32[0] = le32_to_cpu(f->j32[0]);
+ to->u32[1] = le32_to_cpu(f->j32[1]);
+}
+
+/**
+ * key_write - transform a key from in-memory format.
+ * @c: UBIFS file-system description object
+ * @from: the key to transform
+ * @to: the key to store the result
+ */
+static inline void key_write(const struct ubifs_info *c,
+ const union ubifs_key *from, void *to)
+{
+ union ubifs_key *t = to;
+
+ t->j32[0] = cpu_to_le32(from->u32[0]);
+ t->j32[1] = cpu_to_le32(from->u32[1]);
+ memset(to + 8, 0, UBIFS_MAX_KEY_LEN - 8);
+}
+
+/**
+ * key_write_idx - transform a key from in-memory format for the index.
+ * @c: UBIFS file-system description object
+ * @from: the key to transform
+ * @to: the key to store the result
+ */
+static inline void key_write_idx(const struct ubifs_info *c,
+ const union ubifs_key *from, void *to)
+{
+ union ubifs_key *t = to;
+
+ t->j32[0] = cpu_to_le32(from->u32[0]);
+ t->j32[1] = cpu_to_le32(from->u32[1]);
+}
+
+/**
+ * key_copy - copy a key.
+ * @c: UBIFS file-system description object
+ * @from: the key to copy from
+ * @to: the key to copy to
+ */
+static inline void key_copy(const struct ubifs_info *c,
+ const union ubifs_key *from, union ubifs_key *to)
+{
+ to->u64[0] = from->u64[0];
+}
+
+/**
+ * keys_cmp - compare keys.
+ * @c: UBIFS file-system description object
+ * @key1: the first key to compare
+ * @key2: the second key to compare
+ *
+ * This function compares 2 keys and returns %-1 if @key1 is less than
+ * @key2, %0 if the keys are equivalent and %1 if @key1 is greater than @key2.
+ */
+static inline int keys_cmp(const struct ubifs_info *c,
+ const union ubifs_key *key1,
+ const union ubifs_key *key2)
+{
+ if (key1->u32[0] < key2->u32[0])
+ return -1;
+ if (key1->u32[0] > key2->u32[0])
+ return 1;
+ if (key1->u32[1] < key2->u32[1])
+ return -1;
+ if (key1->u32[1] > key2->u32[1])
+ return 1;
+
+ return 0;
+}
+
+/**
+ * keys_eq - determine if keys are equivalent.
+ * @c: UBIFS file-system description object
+ * @key1: the first key to compare
+ * @key2: the second key to compare
+ *
+ * This function compares 2 keys and returns %1 if @key1 is equal to @key2 and
+ * %0 if not.
+ */
+static inline int keys_eq(const struct ubifs_info *c,
+ const union ubifs_key *key1,
+ const union ubifs_key *key2)
+{
+ if (key1->u32[0] != key2->u32[0])
+ return 0;
+ if (key1->u32[1] != key2->u32[1])
+ return 0;
+ return 1;
+}
+
+/**
+ * is_hash_key - is a key vulnerable to hash collisions.
+ * @c: UBIFS file-system description object
+ * @key: key
+ *
+ * This function returns %1 if @key is a hashed key or %0 otherwise.
+ */
+static inline int is_hash_key(const struct ubifs_info *c,
+ const union ubifs_key *key)
+{
+ int type = key_type(c, key);
+
+ return type == UBIFS_DENT_KEY || type == UBIFS_XENT_KEY;
+}
+
+/**
+ * key_max_inode_size - get maximum file size allowed by current key format.
+ * @c: UBIFS file-system description object
+ */
+static inline unsigned long long key_max_inode_size(const struct ubifs_info *c)
+{
+ switch (c->key_fmt) {
+ case UBIFS_SIMPLE_KEY_FMT:
+ return (1ULL << UBIFS_S_KEY_BLOCK_BITS) * UBIFS_BLOCK_SIZE;
+ default:
+ return 0;
+ }
+}
+
+#endif /* !__UBIFS_KEY_H__ */
diff --git a/roms/u-boot/fs/ubifs/log.c b/roms/u-boot/fs/ubifs/log.c
new file mode 100644
index 000000000..77ff52575
--- /dev/null
+++ b/roms/u-boot/fs/ubifs/log.c
@@ -0,0 +1,746 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ *
+ * Authors: Artem Bityutskiy (Битюцкий Артём)
+ * Adrian Hunter
+ */
+
+/*
+ * This file is a part of UBIFS journal implementation and contains various
+ * functions which manipulate the log. The log is a fixed area on the flash
+ * which does not contain any data but refers to buds. The log is a part of the
+ * journal.
+ */
+
+#ifdef __UBOOT__
+#include <log.h>
+#include <dm/devres.h>
+#include <linux/err.h>
+#endif
+#include "ubifs.h"
+
+static int dbg_check_bud_bytes(struct ubifs_info *c);
+
+/**
+ * ubifs_search_bud - search bud LEB.
+ * @c: UBIFS file-system description object
+ * @lnum: logical eraseblock number to search
+ *
+ * This function searches bud LEB @lnum. Returns bud description object in case
+ * of success and %NULL if there is no bud with this LEB number.
+ */
+struct ubifs_bud *ubifs_search_bud(struct ubifs_info *c, int lnum)
+{
+ struct rb_node *p;
+ struct ubifs_bud *bud;
+
+ spin_lock(&c->buds_lock);
+ p = c->buds.rb_node;
+ while (p) {
+ bud = rb_entry(p, struct ubifs_bud, rb);
+ if (lnum < bud->lnum)
+ p = p->rb_left;
+ else if (lnum > bud->lnum)
+ p = p->rb_right;
+ else {
+ spin_unlock(&c->buds_lock);
+ return bud;
+ }
+ }
+ spin_unlock(&c->buds_lock);
+ return NULL;
+}
+
+/**
+ * ubifs_get_wbuf - get the wbuf associated with a LEB, if there is one.
+ * @c: UBIFS file-system description object
+ * @lnum: logical eraseblock number to search
+ *
+ * This functions returns the wbuf for @lnum or %NULL if there is not one.
+ */
+struct ubifs_wbuf *ubifs_get_wbuf(struct ubifs_info *c, int lnum)
+{
+ struct rb_node *p;
+ struct ubifs_bud *bud;
+ int jhead;
+
+ if (!c->jheads)
+ return NULL;
+
+ spin_lock(&c->buds_lock);
+ p = c->buds.rb_node;
+ while (p) {
+ bud = rb_entry(p, struct ubifs_bud, rb);
+ if (lnum < bud->lnum)
+ p = p->rb_left;
+ else if (lnum > bud->lnum)
+ p = p->rb_right;
+ else {
+ jhead = bud->jhead;
+ spin_unlock(&c->buds_lock);
+ return &c->jheads[jhead].wbuf;
+ }
+ }
+ spin_unlock(&c->buds_lock);
+ return NULL;
+}
+
+/**
+ * empty_log_bytes - calculate amount of empty space in the log.
+ * @c: UBIFS file-system description object
+ */
+static inline long long empty_log_bytes(const struct ubifs_info *c)
+{
+ long long h, t;
+
+ h = (long long)c->lhead_lnum * c->leb_size + c->lhead_offs;
+ t = (long long)c->ltail_lnum * c->leb_size;
+
+ if (h > t)
+ return c->log_bytes - h + t;
+ else if (h != t)
+ return t - h;
+ else if (c->lhead_lnum != c->ltail_lnum)
+ return 0;
+ else
+ return c->log_bytes;
+}
+
+/**
+ * ubifs_add_bud - add bud LEB to the tree of buds and its journal head list.
+ * @c: UBIFS file-system description object
+ * @bud: the bud to add
+ */
+void ubifs_add_bud(struct ubifs_info *c, struct ubifs_bud *bud)
+{
+ struct rb_node **p, *parent = NULL;
+ struct ubifs_bud *b;
+ struct ubifs_jhead *jhead;
+
+ spin_lock(&c->buds_lock);
+ p = &c->buds.rb_node;
+ while (*p) {
+ parent = *p;
+ b = rb_entry(parent, struct ubifs_bud, rb);
+ ubifs_assert(bud->lnum != b->lnum);
+ if (bud->lnum < b->lnum)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+
+ rb_link_node(&bud->rb, parent, p);
+ rb_insert_color(&bud->rb, &c->buds);
+ if (c->jheads) {
+ jhead = &c->jheads[bud->jhead];
+ list_add_tail(&bud->list, &jhead->buds_list);
+ } else
+ ubifs_assert(c->replaying && c->ro_mount);
+
+ /*
+ * Note, although this is a new bud, we anyway account this space now,
+ * before any data has been written to it, because this is about to
+ * guarantee fixed mount time, and this bud will anyway be read and
+ * scanned.
+ */
+ c->bud_bytes += c->leb_size - bud->start;
+
+ dbg_log("LEB %d:%d, jhead %s, bud_bytes %lld", bud->lnum,
+ bud->start, dbg_jhead(bud->jhead), c->bud_bytes);
+ spin_unlock(&c->buds_lock);
+}
+
+/**
+ * ubifs_add_bud_to_log - add a new bud to the log.
+ * @c: UBIFS file-system description object
+ * @jhead: journal head the bud belongs to
+ * @lnum: LEB number of the bud
+ * @offs: starting offset of the bud
+ *
+ * This function writes reference node for the new bud LEB @lnum it to the log,
+ * and adds it to the buds tress. It also makes sure that log size does not
+ * exceed the 'c->max_bud_bytes' limit. Returns zero in case of success,
+ * %-EAGAIN if commit is required, and a negative error codes in case of
+ * failure.
+ */
+int ubifs_add_bud_to_log(struct ubifs_info *c, int jhead, int lnum, int offs)
+{
+ int err;
+ struct ubifs_bud *bud;
+ struct ubifs_ref_node *ref;
+
+ bud = kmalloc(sizeof(struct ubifs_bud), GFP_NOFS);
+ if (!bud)
+ return -ENOMEM;
+ ref = kzalloc(c->ref_node_alsz, GFP_NOFS);
+ if (!ref) {
+ kfree(bud);
+ return -ENOMEM;
+ }
+
+ mutex_lock(&c->log_mutex);
+ ubifs_assert(!c->ro_media && !c->ro_mount);
+ if (c->ro_error) {
+ err = -EROFS;
+ goto out_unlock;
+ }
+
+ /* Make sure we have enough space in the log */
+ if (empty_log_bytes(c) - c->ref_node_alsz < c->min_log_bytes) {
+ dbg_log("not enough log space - %lld, required %d",
+ empty_log_bytes(c), c->min_log_bytes);
+ ubifs_commit_required(c);
+ err = -EAGAIN;
+ goto out_unlock;
+ }
+
+ /*
+ * Make sure the amount of space in buds will not exceed the
+ * 'c->max_bud_bytes' limit, because we want to guarantee mount time
+ * limits.
+ *
+ * It is not necessary to hold @c->buds_lock when reading @c->bud_bytes
+ * because we are holding @c->log_mutex. All @c->bud_bytes take place
+ * when both @c->log_mutex and @c->bud_bytes are locked.
+ */
+ if (c->bud_bytes + c->leb_size - offs > c->max_bud_bytes) {
+ dbg_log("bud bytes %lld (%lld max), require commit",
+ c->bud_bytes, c->max_bud_bytes);
+ ubifs_commit_required(c);
+ err = -EAGAIN;
+ goto out_unlock;
+ }
+
+ /*
+ * If the journal is full enough - start background commit. Note, it is
+ * OK to read 'c->cmt_state' without spinlock because integer reads
+ * are atomic in the kernel.
+ */
+ if (c->bud_bytes >= c->bg_bud_bytes &&
+ c->cmt_state == COMMIT_RESTING) {
+ dbg_log("bud bytes %lld (%lld max), initiate BG commit",
+ c->bud_bytes, c->max_bud_bytes);
+ ubifs_request_bg_commit(c);
+ }
+
+ bud->lnum = lnum;
+ bud->start = offs;
+ bud->jhead = jhead;
+
+ ref->ch.node_type = UBIFS_REF_NODE;
+ ref->lnum = cpu_to_le32(bud->lnum);
+ ref->offs = cpu_to_le32(bud->start);
+ ref->jhead = cpu_to_le32(jhead);
+
+ if (c->lhead_offs > c->leb_size - c->ref_node_alsz) {
+ c->lhead_lnum = ubifs_next_log_lnum(c, c->lhead_lnum);
+ ubifs_assert(c->lhead_lnum != c->ltail_lnum);
+ c->lhead_offs = 0;
+ }
+
+ if (c->lhead_offs == 0) {
+ /* Must ensure next log LEB has been unmapped */
+ err = ubifs_leb_unmap(c, c->lhead_lnum);
+ if (err)
+ goto out_unlock;
+ }
+
+ if (bud->start == 0) {
+ /*
+ * Before writing the LEB reference which refers an empty LEB
+ * to the log, we have to make sure it is mapped, because
+ * otherwise we'd risk to refer an LEB with garbage in case of
+ * an unclean reboot, because the target LEB might have been
+ * unmapped, but not yet physically erased.
+ */
+ err = ubifs_leb_map(c, bud->lnum);
+ if (err)
+ goto out_unlock;
+ }
+
+ dbg_log("write ref LEB %d:%d",
+ c->lhead_lnum, c->lhead_offs);
+ err = ubifs_write_node(c, ref, UBIFS_REF_NODE_SZ, c->lhead_lnum,
+ c->lhead_offs);
+ if (err)
+ goto out_unlock;
+
+ c->lhead_offs += c->ref_node_alsz;
+
+ ubifs_add_bud(c, bud);
+
+ mutex_unlock(&c->log_mutex);
+ kfree(ref);
+ return 0;
+
+out_unlock:
+ mutex_unlock(&c->log_mutex);
+ kfree(ref);
+ kfree(bud);
+ return err;
+}
+
+/**
+ * remove_buds - remove used buds.
+ * @c: UBIFS file-system description object
+ *
+ * This function removes use buds from the buds tree. It does not remove the
+ * buds which are pointed to by journal heads.
+ */
+static void remove_buds(struct ubifs_info *c)
+{
+ struct rb_node *p;
+
+ ubifs_assert(list_empty(&c->old_buds));
+ c->cmt_bud_bytes = 0;
+ spin_lock(&c->buds_lock);
+ p = rb_first(&c->buds);
+ while (p) {
+ struct rb_node *p1 = p;
+ struct ubifs_bud *bud;
+ struct ubifs_wbuf *wbuf;
+
+ p = rb_next(p);
+ bud = rb_entry(p1, struct ubifs_bud, rb);
+ wbuf = &c->jheads[bud->jhead].wbuf;
+
+ if (wbuf->lnum == bud->lnum) {
+ /*
+ * Do not remove buds which are pointed to by journal
+ * heads (non-closed buds).
+ */
+ c->cmt_bud_bytes += wbuf->offs - bud->start;
+ dbg_log("preserve %d:%d, jhead %s, bud bytes %d, cmt_bud_bytes %lld",
+ bud->lnum, bud->start, dbg_jhead(bud->jhead),
+ wbuf->offs - bud->start, c->cmt_bud_bytes);
+ bud->start = wbuf->offs;
+ } else {
+ c->cmt_bud_bytes += c->leb_size - bud->start;
+ dbg_log("remove %d:%d, jhead %s, bud bytes %d, cmt_bud_bytes %lld",
+ bud->lnum, bud->start, dbg_jhead(bud->jhead),
+ c->leb_size - bud->start, c->cmt_bud_bytes);
+ rb_erase(p1, &c->buds);
+ /*
+ * If the commit does not finish, the recovery will need
+ * to replay the journal, in which case the old buds
+ * must be unchanged. Do not release them until post
+ * commit i.e. do not allow them to be garbage
+ * collected.
+ */
+ list_move(&bud->list, &c->old_buds);
+ }
+ }
+ spin_unlock(&c->buds_lock);
+}
+
+/**
+ * ubifs_log_start_commit - start commit.
+ * @c: UBIFS file-system description object
+ * @ltail_lnum: return new log tail LEB number
+ *
+ * The commit operation starts with writing "commit start" node to the log and
+ * reference nodes for all journal heads which will define new journal after
+ * the commit has been finished. The commit start and reference nodes are
+ * written in one go to the nearest empty log LEB (hence, when commit is
+ * finished UBIFS may safely unmap all the previous log LEBs). This function
+ * returns zero in case of success and a negative error code in case of
+ * failure.
+ */
+int ubifs_log_start_commit(struct ubifs_info *c, int *ltail_lnum)
+{
+ void *buf;
+ struct ubifs_cs_node *cs;
+ struct ubifs_ref_node *ref;
+ int err, i, max_len, len;
+
+ err = dbg_check_bud_bytes(c);
+ if (err)
+ return err;
+
+ max_len = UBIFS_CS_NODE_SZ + c->jhead_cnt * UBIFS_REF_NODE_SZ;
+ max_len = ALIGN(max_len, c->min_io_size);
+ buf = cs = kmalloc(max_len, GFP_NOFS);
+ if (!buf)
+ return -ENOMEM;
+
+ cs->ch.node_type = UBIFS_CS_NODE;
+ cs->cmt_no = cpu_to_le64(c->cmt_no);
+ ubifs_prepare_node(c, cs, UBIFS_CS_NODE_SZ, 0);
+
+ /*
+ * Note, we do not lock 'c->log_mutex' because this is the commit start
+ * phase and we are exclusively using the log. And we do not lock
+ * write-buffer because nobody can write to the file-system at this
+ * phase.
+ */
+
+ len = UBIFS_CS_NODE_SZ;
+ for (i = 0; i < c->jhead_cnt; i++) {
+ int lnum = c->jheads[i].wbuf.lnum;
+ int offs = c->jheads[i].wbuf.offs;
+
+ if (lnum == -1 || offs == c->leb_size)
+ continue;
+
+ dbg_log("add ref to LEB %d:%d for jhead %s",
+ lnum, offs, dbg_jhead(i));
+ ref = buf + len;
+ ref->ch.node_type = UBIFS_REF_NODE;
+ ref->lnum = cpu_to_le32(lnum);
+ ref->offs = cpu_to_le32(offs);
+ ref->jhead = cpu_to_le32(i);
+
+ ubifs_prepare_node(c, ref, UBIFS_REF_NODE_SZ, 0);
+ len += UBIFS_REF_NODE_SZ;
+ }
+
+ ubifs_pad(c, buf + len, ALIGN(len, c->min_io_size) - len);
+
+ /* Switch to the next log LEB */
+ if (c->lhead_offs) {
+ c->lhead_lnum = ubifs_next_log_lnum(c, c->lhead_lnum);
+ ubifs_assert(c->lhead_lnum != c->ltail_lnum);
+ c->lhead_offs = 0;
+ }
+
+ /* Must ensure next LEB has been unmapped */
+ err = ubifs_leb_unmap(c, c->lhead_lnum);
+ if (err)
+ goto out;
+
+ len = ALIGN(len, c->min_io_size);
+ dbg_log("writing commit start at LEB %d:0, len %d", c->lhead_lnum, len);
+ err = ubifs_leb_write(c, c->lhead_lnum, cs, 0, len);
+ if (err)
+ goto out;
+
+ *ltail_lnum = c->lhead_lnum;
+
+ c->lhead_offs += len;
+ if (c->lhead_offs == c->leb_size) {
+ c->lhead_lnum = ubifs_next_log_lnum(c, c->lhead_lnum);
+ c->lhead_offs = 0;
+ }
+
+ remove_buds(c);
+
+ /*
+ * We have started the commit and now users may use the rest of the log
+ * for new writes.
+ */
+ c->min_log_bytes = 0;
+
+out:
+ kfree(buf);
+ return err;
+}
+
+/**
+ * ubifs_log_end_commit - end commit.
+ * @c: UBIFS file-system description object
+ * @ltail_lnum: new log tail LEB number
+ *
+ * This function is called on when the commit operation was finished. It
+ * moves log tail to new position and updates the master node so that it stores
+ * the new log tail LEB number. Returns zero in case of success and a negative
+ * error code in case of failure.
+ */
+int ubifs_log_end_commit(struct ubifs_info *c, int ltail_lnum)
+{
+ int err;
+
+ /*
+ * At this phase we have to lock 'c->log_mutex' because UBIFS allows FS
+ * writes during commit. Its only short "commit" start phase when
+ * writers are blocked.
+ */
+ mutex_lock(&c->log_mutex);
+
+ dbg_log("old tail was LEB %d:0, new tail is LEB %d:0",
+ c->ltail_lnum, ltail_lnum);
+
+ c->ltail_lnum = ltail_lnum;
+ /*
+ * The commit is finished and from now on it must be guaranteed that
+ * there is always enough space for the next commit.
+ */
+ c->min_log_bytes = c->leb_size;
+
+ spin_lock(&c->buds_lock);
+ c->bud_bytes -= c->cmt_bud_bytes;
+ spin_unlock(&c->buds_lock);
+
+ err = dbg_check_bud_bytes(c);
+ if (err)
+ goto out;
+
+ err = ubifs_write_master(c);
+
+out:
+ mutex_unlock(&c->log_mutex);
+ return err;
+}
+
+/**
+ * ubifs_log_post_commit - things to do after commit is completed.
+ * @c: UBIFS file-system description object
+ * @old_ltail_lnum: old log tail LEB number
+ *
+ * Release buds only after commit is completed, because they must be unchanged
+ * if recovery is needed.
+ *
+ * Unmap log LEBs only after commit is completed, because they may be needed for
+ * recovery.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int ubifs_log_post_commit(struct ubifs_info *c, int old_ltail_lnum)
+{
+ int lnum, err = 0;
+
+ while (!list_empty(&c->old_buds)) {
+ struct ubifs_bud *bud;
+
+ bud = list_entry(c->old_buds.next, struct ubifs_bud, list);
+ err = ubifs_return_leb(c, bud->lnum);
+ if (err)
+ return err;
+ list_del(&bud->list);
+ kfree(bud);
+ }
+ mutex_lock(&c->log_mutex);
+ for (lnum = old_ltail_lnum; lnum != c->ltail_lnum;
+ lnum = ubifs_next_log_lnum(c, lnum)) {
+ dbg_log("unmap log LEB %d", lnum);
+ err = ubifs_leb_unmap(c, lnum);
+ if (err)
+ goto out;
+ }
+out:
+ mutex_unlock(&c->log_mutex);
+ return err;
+}
+
+/**
+ * struct done_ref - references that have been done.
+ * @rb: rb-tree node
+ * @lnum: LEB number
+ */
+struct done_ref {
+ struct rb_node rb;
+ int lnum;
+};
+
+/**
+ * done_already - determine if a reference has been done already.
+ * @done_tree: rb-tree to store references that have been done
+ * @lnum: LEB number of reference
+ *
+ * This function returns %1 if the reference has been done, %0 if not, otherwise
+ * a negative error code is returned.
+ */
+static int done_already(struct rb_root *done_tree, int lnum)
+{
+ struct rb_node **p = &done_tree->rb_node, *parent = NULL;
+ struct done_ref *dr;
+
+ while (*p) {
+ parent = *p;
+ dr = rb_entry(parent, struct done_ref, rb);
+ if (lnum < dr->lnum)
+ p = &(*p)->rb_left;
+ else if (lnum > dr->lnum)
+ p = &(*p)->rb_right;
+ else
+ return 1;
+ }
+
+ dr = kzalloc(sizeof(struct done_ref), GFP_NOFS);
+ if (!dr)
+ return -ENOMEM;
+
+ dr->lnum = lnum;
+
+ rb_link_node(&dr->rb, parent, p);
+ rb_insert_color(&dr->rb, done_tree);
+
+ return 0;
+}
+
+/**
+ * destroy_done_tree - destroy the done tree.
+ * @done_tree: done tree to destroy
+ */
+static void destroy_done_tree(struct rb_root *done_tree)
+{
+ struct done_ref *dr, *n;
+
+ rbtree_postorder_for_each_entry_safe(dr, n, done_tree, rb)
+ kfree(dr);
+}
+
+/**
+ * add_node - add a node to the consolidated log.
+ * @c: UBIFS file-system description object
+ * @buf: buffer to which to add
+ * @lnum: LEB number to which to write is passed and returned here
+ * @offs: offset to where to write is passed and returned here
+ * @node: node to add
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int add_node(struct ubifs_info *c, void *buf, int *lnum, int *offs,
+ void *node)
+{
+ struct ubifs_ch *ch = node;
+ int len = le32_to_cpu(ch->len), remains = c->leb_size - *offs;
+
+ if (len > remains) {
+ int sz = ALIGN(*offs, c->min_io_size), err;
+
+ ubifs_pad(c, buf + *offs, sz - *offs);
+ err = ubifs_leb_change(c, *lnum, buf, sz);
+ if (err)
+ return err;
+ *lnum = ubifs_next_log_lnum(c, *lnum);
+ *offs = 0;
+ }
+ memcpy(buf + *offs, node, len);
+ *offs += ALIGN(len, 8);
+ return 0;
+}
+
+/**
+ * ubifs_consolidate_log - consolidate the log.
+ * @c: UBIFS file-system description object
+ *
+ * Repeated failed commits could cause the log to be full, but at least 1 LEB is
+ * needed for commit. This function rewrites the reference nodes in the log
+ * omitting duplicates, and failed CS nodes, and leaving no gaps.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int ubifs_consolidate_log(struct ubifs_info *c)
+{
+ struct ubifs_scan_leb *sleb;
+ struct ubifs_scan_node *snod;
+ struct rb_root done_tree = RB_ROOT;
+ int lnum, err, first = 1, write_lnum, offs = 0;
+ void *buf;
+
+ dbg_rcvry("log tail LEB %d, log head LEB %d", c->ltail_lnum,
+ c->lhead_lnum);
+ buf = vmalloc(c->leb_size);
+ if (!buf)
+ return -ENOMEM;
+ lnum = c->ltail_lnum;
+ write_lnum = lnum;
+ while (1) {
+ sleb = ubifs_scan(c, lnum, 0, c->sbuf, 0);
+ if (IS_ERR(sleb)) {
+ err = PTR_ERR(sleb);
+ goto out_free;
+ }
+ list_for_each_entry(snod, &sleb->nodes, list) {
+ switch (snod->type) {
+ case UBIFS_REF_NODE: {
+ struct ubifs_ref_node *ref = snod->node;
+ int ref_lnum = le32_to_cpu(ref->lnum);
+
+ err = done_already(&done_tree, ref_lnum);
+ if (err < 0)
+ goto out_scan;
+ if (err != 1) {
+ err = add_node(c, buf, &write_lnum,
+ &offs, snod->node);
+ if (err)
+ goto out_scan;
+ }
+ break;
+ }
+ case UBIFS_CS_NODE:
+ if (!first)
+ break;
+ err = add_node(c, buf, &write_lnum, &offs,
+ snod->node);
+ if (err)
+ goto out_scan;
+ first = 0;
+ break;
+ }
+ }
+ ubifs_scan_destroy(sleb);
+ if (lnum == c->lhead_lnum)
+ break;
+ lnum = ubifs_next_log_lnum(c, lnum);
+ }
+ if (offs) {
+ int sz = ALIGN(offs, c->min_io_size);
+
+ ubifs_pad(c, buf + offs, sz - offs);
+ err = ubifs_leb_change(c, write_lnum, buf, sz);
+ if (err)
+ goto out_free;
+ offs = ALIGN(offs, c->min_io_size);
+ }
+ destroy_done_tree(&done_tree);
+ vfree(buf);
+ if (write_lnum == c->lhead_lnum) {
+ ubifs_err(c, "log is too full");
+ return -EINVAL;
+ }
+ /* Unmap remaining LEBs */
+ lnum = write_lnum;
+ do {
+ lnum = ubifs_next_log_lnum(c, lnum);
+ err = ubifs_leb_unmap(c, lnum);
+ if (err)
+ return err;
+ } while (lnum != c->lhead_lnum);
+ c->lhead_lnum = write_lnum;
+ c->lhead_offs = offs;
+ dbg_rcvry("new log head at %d:%d", c->lhead_lnum, c->lhead_offs);
+ return 0;
+
+out_scan:
+ ubifs_scan_destroy(sleb);
+out_free:
+ destroy_done_tree(&done_tree);
+ vfree(buf);
+ return err;
+}
+
+/**
+ * dbg_check_bud_bytes - make sure bud bytes calculation are all right.
+ * @c: UBIFS file-system description object
+ *
+ * This function makes sure the amount of flash space used by closed buds
+ * ('c->bud_bytes' is correct). Returns zero in case of success and %-EINVAL in
+ * case of failure.
+ */
+static int dbg_check_bud_bytes(struct ubifs_info *c)
+{
+ int i, err = 0;
+ struct ubifs_bud *bud;
+ long long bud_bytes = 0;
+
+ if (!dbg_is_chk_gen(c))
+ return 0;
+
+ spin_lock(&c->buds_lock);
+ for (i = 0; i < c->jhead_cnt; i++)
+ list_for_each_entry(bud, &c->jheads[i].buds_list, list)
+ bud_bytes += c->leb_size - bud->start;
+
+ if (c->bud_bytes != bud_bytes) {
+ ubifs_err(c, "bad bud_bytes %lld, calculated %lld",
+ c->bud_bytes, bud_bytes);
+ err = -EINVAL;
+ }
+ spin_unlock(&c->buds_lock);
+
+ return err;
+}
diff --git a/roms/u-boot/fs/ubifs/lprops.c b/roms/u-boot/fs/ubifs/lprops.c
new file mode 100644
index 000000000..a9e22abe6
--- /dev/null
+++ b/roms/u-boot/fs/ubifs/lprops.c
@@ -0,0 +1,1314 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ *
+ * Authors: Adrian Hunter
+ * Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * This file implements the functions that access LEB properties and their
+ * categories. LEBs are categorized based on the needs of UBIFS, and the
+ * categories are stored as either heaps or lists to provide a fast way of
+ * finding a LEB in a particular category. For example, UBIFS may need to find
+ * an empty LEB for the journal, or a very dirty LEB for garbage collection.
+ */
+
+#ifdef __UBOOT__
+#include <log.h>
+#include <malloc.h>
+#include <linux/err.h>
+#endif
+#include "ubifs.h"
+
+/**
+ * get_heap_comp_val - get the LEB properties value for heap comparisons.
+ * @lprops: LEB properties
+ * @cat: LEB category
+ */
+static int get_heap_comp_val(struct ubifs_lprops *lprops, int cat)
+{
+ switch (cat) {
+ case LPROPS_FREE:
+ return lprops->free;
+ case LPROPS_DIRTY_IDX:
+ return lprops->free + lprops->dirty;
+ default:
+ return lprops->dirty;
+ }
+}
+
+/**
+ * move_up_lpt_heap - move a new heap entry up as far as possible.
+ * @c: UBIFS file-system description object
+ * @heap: LEB category heap
+ * @lprops: LEB properties to move
+ * @cat: LEB category
+ *
+ * New entries to a heap are added at the bottom and then moved up until the
+ * parent's value is greater. In the case of LPT's category heaps, the value
+ * is either the amount of free space or the amount of dirty space, depending
+ * on the category.
+ */
+static void move_up_lpt_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap,
+ struct ubifs_lprops *lprops, int cat)
+{
+ int val1, val2, hpos;
+
+ hpos = lprops->hpos;
+ if (!hpos)
+ return; /* Already top of the heap */
+ val1 = get_heap_comp_val(lprops, cat);
+ /* Compare to parent and, if greater, move up the heap */
+ do {
+ int ppos = (hpos - 1) / 2;
+
+ val2 = get_heap_comp_val(heap->arr[ppos], cat);
+ if (val2 >= val1)
+ return;
+ /* Greater than parent so move up */
+ heap->arr[ppos]->hpos = hpos;
+ heap->arr[hpos] = heap->arr[ppos];
+ heap->arr[ppos] = lprops;
+ lprops->hpos = ppos;
+ hpos = ppos;
+ } while (hpos);
+}
+
+/**
+ * adjust_lpt_heap - move a changed heap entry up or down the heap.
+ * @c: UBIFS file-system description object
+ * @heap: LEB category heap
+ * @lprops: LEB properties to move
+ * @hpos: heap position of @lprops
+ * @cat: LEB category
+ *
+ * Changed entries in a heap are moved up or down until the parent's value is
+ * greater. In the case of LPT's category heaps, the value is either the amount
+ * of free space or the amount of dirty space, depending on the category.
+ */
+static void adjust_lpt_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap,
+ struct ubifs_lprops *lprops, int hpos, int cat)
+{
+ int val1, val2, val3, cpos;
+
+ val1 = get_heap_comp_val(lprops, cat);
+ /* Compare to parent and, if greater than parent, move up the heap */
+ if (hpos) {
+ int ppos = (hpos - 1) / 2;
+
+ val2 = get_heap_comp_val(heap->arr[ppos], cat);
+ if (val1 > val2) {
+ /* Greater than parent so move up */
+ while (1) {
+ heap->arr[ppos]->hpos = hpos;
+ heap->arr[hpos] = heap->arr[ppos];
+ heap->arr[ppos] = lprops;
+ lprops->hpos = ppos;
+ hpos = ppos;
+ if (!hpos)
+ return;
+ ppos = (hpos - 1) / 2;
+ val2 = get_heap_comp_val(heap->arr[ppos], cat);
+ if (val1 <= val2)
+ return;
+ /* Still greater than parent so keep going */
+ }
+ }
+ }
+
+ /* Not greater than parent, so compare to children */
+ while (1) {
+ /* Compare to left child */
+ cpos = hpos * 2 + 1;
+ if (cpos >= heap->cnt)
+ return;
+ val2 = get_heap_comp_val(heap->arr[cpos], cat);
+ if (val1 < val2) {
+ /* Less than left child, so promote biggest child */
+ if (cpos + 1 < heap->cnt) {
+ val3 = get_heap_comp_val(heap->arr[cpos + 1],
+ cat);
+ if (val3 > val2)
+ cpos += 1; /* Right child is bigger */
+ }
+ heap->arr[cpos]->hpos = hpos;
+ heap->arr[hpos] = heap->arr[cpos];
+ heap->arr[cpos] = lprops;
+ lprops->hpos = cpos;
+ hpos = cpos;
+ continue;
+ }
+ /* Compare to right child */
+ cpos += 1;
+ if (cpos >= heap->cnt)
+ return;
+ val3 = get_heap_comp_val(heap->arr[cpos], cat);
+ if (val1 < val3) {
+ /* Less than right child, so promote right child */
+ heap->arr[cpos]->hpos = hpos;
+ heap->arr[hpos] = heap->arr[cpos];
+ heap->arr[cpos] = lprops;
+ lprops->hpos = cpos;
+ hpos = cpos;
+ continue;
+ }
+ return;
+ }
+}
+
+/**
+ * add_to_lpt_heap - add LEB properties to a LEB category heap.
+ * @c: UBIFS file-system description object
+ * @lprops: LEB properties to add
+ * @cat: LEB category
+ *
+ * This function returns %1 if @lprops is added to the heap for LEB category
+ * @cat, otherwise %0 is returned because the heap is full.
+ */
+static int add_to_lpt_heap(struct ubifs_info *c, struct ubifs_lprops *lprops,
+ int cat)
+{
+ struct ubifs_lpt_heap *heap = &c->lpt_heap[cat - 1];
+
+ if (heap->cnt >= heap->max_cnt) {
+ const int b = LPT_HEAP_SZ / 2 - 1;
+ int cpos, val1, val2;
+
+ /* Compare to some other LEB on the bottom of heap */
+ /* Pick a position kind of randomly */
+ cpos = (((size_t)lprops >> 4) & b) + b;
+ ubifs_assert(cpos >= b);
+ ubifs_assert(cpos < LPT_HEAP_SZ);
+ ubifs_assert(cpos < heap->cnt);
+
+ val1 = get_heap_comp_val(lprops, cat);
+ val2 = get_heap_comp_val(heap->arr[cpos], cat);
+ if (val1 > val2) {
+ struct ubifs_lprops *lp;
+
+ lp = heap->arr[cpos];
+ lp->flags &= ~LPROPS_CAT_MASK;
+ lp->flags |= LPROPS_UNCAT;
+ list_add(&lp->list, &c->uncat_list);
+ lprops->hpos = cpos;
+ heap->arr[cpos] = lprops;
+ move_up_lpt_heap(c, heap, lprops, cat);
+ dbg_check_heap(c, heap, cat, lprops->hpos);
+ return 1; /* Added to heap */
+ }
+ dbg_check_heap(c, heap, cat, -1);
+ return 0; /* Not added to heap */
+ } else {
+ lprops->hpos = heap->cnt++;
+ heap->arr[lprops->hpos] = lprops;
+ move_up_lpt_heap(c, heap, lprops, cat);
+ dbg_check_heap(c, heap, cat, lprops->hpos);
+ return 1; /* Added to heap */
+ }
+}
+
+/**
+ * remove_from_lpt_heap - remove LEB properties from a LEB category heap.
+ * @c: UBIFS file-system description object
+ * @lprops: LEB properties to remove
+ * @cat: LEB category
+ */
+static void remove_from_lpt_heap(struct ubifs_info *c,
+ struct ubifs_lprops *lprops, int cat)
+{
+ struct ubifs_lpt_heap *heap;
+ int hpos = lprops->hpos;
+
+ heap = &c->lpt_heap[cat - 1];
+ ubifs_assert(hpos >= 0 && hpos < heap->cnt);
+ ubifs_assert(heap->arr[hpos] == lprops);
+ heap->cnt -= 1;
+ if (hpos < heap->cnt) {
+ heap->arr[hpos] = heap->arr[heap->cnt];
+ heap->arr[hpos]->hpos = hpos;
+ adjust_lpt_heap(c, heap, heap->arr[hpos], hpos, cat);
+ }
+ dbg_check_heap(c, heap, cat, -1);
+}
+
+/**
+ * lpt_heap_replace - replace lprops in a category heap.
+ * @c: UBIFS file-system description object
+ * @old_lprops: LEB properties to replace
+ * @new_lprops: LEB properties with which to replace
+ * @cat: LEB category
+ *
+ * During commit it is sometimes necessary to copy a pnode (see dirty_cow_pnode)
+ * and the lprops that the pnode contains. When that happens, references in
+ * the category heaps to those lprops must be updated to point to the new
+ * lprops. This function does that.
+ */
+static void lpt_heap_replace(struct ubifs_info *c,
+ struct ubifs_lprops *old_lprops,
+ struct ubifs_lprops *new_lprops, int cat)
+{
+ struct ubifs_lpt_heap *heap;
+ int hpos = new_lprops->hpos;
+
+ heap = &c->lpt_heap[cat - 1];
+ heap->arr[hpos] = new_lprops;
+}
+
+/**
+ * ubifs_add_to_cat - add LEB properties to a category list or heap.
+ * @c: UBIFS file-system description object
+ * @lprops: LEB properties to add
+ * @cat: LEB category to which to add
+ *
+ * LEB properties are categorized to enable fast find operations.
+ */
+void ubifs_add_to_cat(struct ubifs_info *c, struct ubifs_lprops *lprops,
+ int cat)
+{
+ switch (cat) {
+ case LPROPS_DIRTY:
+ case LPROPS_DIRTY_IDX:
+ case LPROPS_FREE:
+ if (add_to_lpt_heap(c, lprops, cat))
+ break;
+ /* No more room on heap so make it un-categorized */
+ cat = LPROPS_UNCAT;
+ /* Fall through */
+ case LPROPS_UNCAT:
+ list_add(&lprops->list, &c->uncat_list);
+ break;
+ case LPROPS_EMPTY:
+ list_add(&lprops->list, &c->empty_list);
+ break;
+ case LPROPS_FREEABLE:
+ list_add(&lprops->list, &c->freeable_list);
+ c->freeable_cnt += 1;
+ break;
+ case LPROPS_FRDI_IDX:
+ list_add(&lprops->list, &c->frdi_idx_list);
+ break;
+ default:
+ ubifs_assert(0);
+ }
+
+ lprops->flags &= ~LPROPS_CAT_MASK;
+ lprops->flags |= cat;
+ c->in_a_category_cnt += 1;
+ ubifs_assert(c->in_a_category_cnt <= c->main_lebs);
+}
+
+/**
+ * ubifs_remove_from_cat - remove LEB properties from a category list or heap.
+ * @c: UBIFS file-system description object
+ * @lprops: LEB properties to remove
+ * @cat: LEB category from which to remove
+ *
+ * LEB properties are categorized to enable fast find operations.
+ */
+static void ubifs_remove_from_cat(struct ubifs_info *c,
+ struct ubifs_lprops *lprops, int cat)
+{
+ switch (cat) {
+ case LPROPS_DIRTY:
+ case LPROPS_DIRTY_IDX:
+ case LPROPS_FREE:
+ remove_from_lpt_heap(c, lprops, cat);
+ break;
+ case LPROPS_FREEABLE:
+ c->freeable_cnt -= 1;
+ ubifs_assert(c->freeable_cnt >= 0);
+ /* Fall through */
+ case LPROPS_UNCAT:
+ case LPROPS_EMPTY:
+ case LPROPS_FRDI_IDX:
+ ubifs_assert(!list_empty(&lprops->list));
+ list_del(&lprops->list);
+ break;
+ default:
+ ubifs_assert(0);
+ }
+
+ c->in_a_category_cnt -= 1;
+ ubifs_assert(c->in_a_category_cnt >= 0);
+}
+
+/**
+ * ubifs_replace_cat - replace lprops in a category list or heap.
+ * @c: UBIFS file-system description object
+ * @old_lprops: LEB properties to replace
+ * @new_lprops: LEB properties with which to replace
+ *
+ * During commit it is sometimes necessary to copy a pnode (see dirty_cow_pnode)
+ * and the lprops that the pnode contains. When that happens, references in
+ * category lists and heaps must be replaced. This function does that.
+ */
+void ubifs_replace_cat(struct ubifs_info *c, struct ubifs_lprops *old_lprops,
+ struct ubifs_lprops *new_lprops)
+{
+ int cat;
+
+ cat = new_lprops->flags & LPROPS_CAT_MASK;
+ switch (cat) {
+ case LPROPS_DIRTY:
+ case LPROPS_DIRTY_IDX:
+ case LPROPS_FREE:
+ lpt_heap_replace(c, old_lprops, new_lprops, cat);
+ break;
+ case LPROPS_UNCAT:
+ case LPROPS_EMPTY:
+ case LPROPS_FREEABLE:
+ case LPROPS_FRDI_IDX:
+ list_replace(&old_lprops->list, &new_lprops->list);
+ break;
+ default:
+ ubifs_assert(0);
+ }
+}
+
+/**
+ * ubifs_ensure_cat - ensure LEB properties are categorized.
+ * @c: UBIFS file-system description object
+ * @lprops: LEB properties
+ *
+ * A LEB may have fallen off of the bottom of a heap, and ended up as
+ * un-categorized even though it has enough space for us now. If that is the
+ * case this function will put the LEB back onto a heap.
+ */
+void ubifs_ensure_cat(struct ubifs_info *c, struct ubifs_lprops *lprops)
+{
+ int cat = lprops->flags & LPROPS_CAT_MASK;
+
+ if (cat != LPROPS_UNCAT)
+ return;
+ cat = ubifs_categorize_lprops(c, lprops);
+ if (cat == LPROPS_UNCAT)
+ return;
+ ubifs_remove_from_cat(c, lprops, LPROPS_UNCAT);
+ ubifs_add_to_cat(c, lprops, cat);
+}
+
+/**
+ * ubifs_categorize_lprops - categorize LEB properties.
+ * @c: UBIFS file-system description object
+ * @lprops: LEB properties to categorize
+ *
+ * LEB properties are categorized to enable fast find operations. This function
+ * returns the LEB category to which the LEB properties belong. Note however
+ * that if the LEB category is stored as a heap and the heap is full, the
+ * LEB properties may have their category changed to %LPROPS_UNCAT.
+ */
+int ubifs_categorize_lprops(const struct ubifs_info *c,
+ const struct ubifs_lprops *lprops)
+{
+ if (lprops->flags & LPROPS_TAKEN)
+ return LPROPS_UNCAT;
+
+ if (lprops->free == c->leb_size) {
+ ubifs_assert(!(lprops->flags & LPROPS_INDEX));
+ return LPROPS_EMPTY;
+ }
+
+ if (lprops->free + lprops->dirty == c->leb_size) {
+ if (lprops->flags & LPROPS_INDEX)
+ return LPROPS_FRDI_IDX;
+ else
+ return LPROPS_FREEABLE;
+ }
+
+ if (lprops->flags & LPROPS_INDEX) {
+ if (lprops->dirty + lprops->free >= c->min_idx_node_sz)
+ return LPROPS_DIRTY_IDX;
+ } else {
+ if (lprops->dirty >= c->dead_wm &&
+ lprops->dirty > lprops->free)
+ return LPROPS_DIRTY;
+ if (lprops->free > 0)
+ return LPROPS_FREE;
+ }
+
+ return LPROPS_UNCAT;
+}
+
+/**
+ * change_category - change LEB properties category.
+ * @c: UBIFS file-system description object
+ * @lprops: LEB properties to re-categorize
+ *
+ * LEB properties are categorized to enable fast find operations. When the LEB
+ * properties change they must be re-categorized.
+ */
+static void change_category(struct ubifs_info *c, struct ubifs_lprops *lprops)
+{
+ int old_cat = lprops->flags & LPROPS_CAT_MASK;
+ int new_cat = ubifs_categorize_lprops(c, lprops);
+
+ if (old_cat == new_cat) {
+ struct ubifs_lpt_heap *heap;
+
+ /* lprops on a heap now must be moved up or down */
+ if (new_cat < 1 || new_cat > LPROPS_HEAP_CNT)
+ return; /* Not on a heap */
+ heap = &c->lpt_heap[new_cat - 1];
+ adjust_lpt_heap(c, heap, lprops, lprops->hpos, new_cat);
+ } else {
+ ubifs_remove_from_cat(c, lprops, old_cat);
+ ubifs_add_to_cat(c, lprops, new_cat);
+ }
+}
+
+/**
+ * ubifs_calc_dark - calculate LEB dark space size.
+ * @c: the UBIFS file-system description object
+ * @spc: amount of free and dirty space in the LEB
+ *
+ * This function calculates and returns amount of dark space in an LEB which
+ * has @spc bytes of free and dirty space.
+ *
+ * UBIFS is trying to account the space which might not be usable, and this
+ * space is called "dark space". For example, if an LEB has only %512 free
+ * bytes, it is dark space, because it cannot fit a large data node.
+ */
+int ubifs_calc_dark(const struct ubifs_info *c, int spc)
+{
+ ubifs_assert(!(spc & 7));
+
+ if (spc < c->dark_wm)
+ return spc;
+
+ /*
+ * If we have slightly more space then the dark space watermark, we can
+ * anyway safely assume it we'll be able to write a node of the
+ * smallest size there.
+ */
+ if (spc - c->dark_wm < MIN_WRITE_SZ)
+ return spc - MIN_WRITE_SZ;
+
+ return c->dark_wm;
+}
+
+/**
+ * is_lprops_dirty - determine if LEB properties are dirty.
+ * @c: the UBIFS file-system description object
+ * @lprops: LEB properties to test
+ */
+static int is_lprops_dirty(struct ubifs_info *c, struct ubifs_lprops *lprops)
+{
+ struct ubifs_pnode *pnode;
+ int pos;
+
+ pos = (lprops->lnum - c->main_first) & (UBIFS_LPT_FANOUT - 1);
+ pnode = (struct ubifs_pnode *)container_of(lprops - pos,
+ struct ubifs_pnode,
+ lprops[0]);
+ return !test_bit(COW_CNODE, &pnode->flags) &&
+ test_bit(DIRTY_CNODE, &pnode->flags);
+}
+
+/**
+ * ubifs_change_lp - change LEB properties.
+ * @c: the UBIFS file-system description object
+ * @lp: LEB properties to change
+ * @free: new free space amount
+ * @dirty: new dirty space amount
+ * @flags: new flags
+ * @idx_gc_cnt: change to the count of @idx_gc list
+ *
+ * This function changes LEB properties (@free, @dirty or @flag). However, the
+ * property which has the %LPROPS_NC value is not changed. Returns a pointer to
+ * the updated LEB properties on success and a negative error code on failure.
+ *
+ * Note, the LEB properties may have had to be copied (due to COW) and
+ * consequently the pointer returned may not be the same as the pointer
+ * passed.
+ */
+const struct ubifs_lprops *ubifs_change_lp(struct ubifs_info *c,
+ const struct ubifs_lprops *lp,
+ int free, int dirty, int flags,
+ int idx_gc_cnt)
+{
+ /*
+ * This is the only function that is allowed to change lprops, so we
+ * discard the "const" qualifier.
+ */
+ struct ubifs_lprops *lprops = (struct ubifs_lprops *)lp;
+
+ dbg_lp("LEB %d, free %d, dirty %d, flags %d",
+ lprops->lnum, free, dirty, flags);
+
+ ubifs_assert(mutex_is_locked(&c->lp_mutex));
+ ubifs_assert(c->lst.empty_lebs >= 0 &&
+ c->lst.empty_lebs <= c->main_lebs);
+ ubifs_assert(c->freeable_cnt >= 0);
+ ubifs_assert(c->freeable_cnt <= c->main_lebs);
+ ubifs_assert(c->lst.taken_empty_lebs >= 0);
+ ubifs_assert(c->lst.taken_empty_lebs <= c->lst.empty_lebs);
+ ubifs_assert(!(c->lst.total_free & 7) && !(c->lst.total_dirty & 7));
+ ubifs_assert(!(c->lst.total_dead & 7) && !(c->lst.total_dark & 7));
+ ubifs_assert(!(c->lst.total_used & 7));
+ ubifs_assert(free == LPROPS_NC || free >= 0);
+ ubifs_assert(dirty == LPROPS_NC || dirty >= 0);
+
+ if (!is_lprops_dirty(c, lprops)) {
+ lprops = ubifs_lpt_lookup_dirty(c, lprops->lnum);
+ if (IS_ERR(lprops))
+ return lprops;
+ } else
+ ubifs_assert(lprops == ubifs_lpt_lookup_dirty(c, lprops->lnum));
+
+ ubifs_assert(!(lprops->free & 7) && !(lprops->dirty & 7));
+
+ spin_lock(&c->space_lock);
+ if ((lprops->flags & LPROPS_TAKEN) && lprops->free == c->leb_size)
+ c->lst.taken_empty_lebs -= 1;
+
+ if (!(lprops->flags & LPROPS_INDEX)) {
+ int old_spc;
+
+ old_spc = lprops->free + lprops->dirty;
+ if (old_spc < c->dead_wm)
+ c->lst.total_dead -= old_spc;
+ else
+ c->lst.total_dark -= ubifs_calc_dark(c, old_spc);
+
+ c->lst.total_used -= c->leb_size - old_spc;
+ }
+
+ if (free != LPROPS_NC) {
+ free = ALIGN(free, 8);
+ c->lst.total_free += free - lprops->free;
+
+ /* Increase or decrease empty LEBs counter if needed */
+ if (free == c->leb_size) {
+ if (lprops->free != c->leb_size)
+ c->lst.empty_lebs += 1;
+ } else if (lprops->free == c->leb_size)
+ c->lst.empty_lebs -= 1;
+ lprops->free = free;
+ }
+
+ if (dirty != LPROPS_NC) {
+ dirty = ALIGN(dirty, 8);
+ c->lst.total_dirty += dirty - lprops->dirty;
+ lprops->dirty = dirty;
+ }
+
+ if (flags != LPROPS_NC) {
+ /* Take care about indexing LEBs counter if needed */
+ if ((lprops->flags & LPROPS_INDEX)) {
+ if (!(flags & LPROPS_INDEX))
+ c->lst.idx_lebs -= 1;
+ } else if (flags & LPROPS_INDEX)
+ c->lst.idx_lebs += 1;
+ lprops->flags = flags;
+ }
+
+ if (!(lprops->flags & LPROPS_INDEX)) {
+ int new_spc;
+
+ new_spc = lprops->free + lprops->dirty;
+ if (new_spc < c->dead_wm)
+ c->lst.total_dead += new_spc;
+ else
+ c->lst.total_dark += ubifs_calc_dark(c, new_spc);
+
+ c->lst.total_used += c->leb_size - new_spc;
+ }
+
+ if ((lprops->flags & LPROPS_TAKEN) && lprops->free == c->leb_size)
+ c->lst.taken_empty_lebs += 1;
+
+ change_category(c, lprops);
+ c->idx_gc_cnt += idx_gc_cnt;
+ spin_unlock(&c->space_lock);
+ return lprops;
+}
+
+/**
+ * ubifs_get_lp_stats - get lprops statistics.
+ * @c: UBIFS file-system description object
+ * @st: return statistics
+ */
+void ubifs_get_lp_stats(struct ubifs_info *c, struct ubifs_lp_stats *lst)
+{
+ spin_lock(&c->space_lock);
+ memcpy(lst, &c->lst, sizeof(struct ubifs_lp_stats));
+ spin_unlock(&c->space_lock);
+}
+
+/**
+ * ubifs_change_one_lp - change LEB properties.
+ * @c: the UBIFS file-system description object
+ * @lnum: LEB to change properties for
+ * @free: amount of free space
+ * @dirty: amount of dirty space
+ * @flags_set: flags to set
+ * @flags_clean: flags to clean
+ * @idx_gc_cnt: change to the count of idx_gc list
+ *
+ * This function changes properties of LEB @lnum. It is a helper wrapper over
+ * 'ubifs_change_lp()' which hides lprops get/release. The arguments are the
+ * same as in case of 'ubifs_change_lp()'. Returns zero in case of success and
+ * a negative error code in case of failure.
+ */
+int ubifs_change_one_lp(struct ubifs_info *c, int lnum, int free, int dirty,
+ int flags_set, int flags_clean, int idx_gc_cnt)
+{
+ int err = 0, flags;
+ const struct ubifs_lprops *lp;
+
+ ubifs_get_lprops(c);
+
+ lp = ubifs_lpt_lookup_dirty(c, lnum);
+ if (IS_ERR(lp)) {
+ err = PTR_ERR(lp);
+ goto out;
+ }
+
+ flags = (lp->flags | flags_set) & ~flags_clean;
+ lp = ubifs_change_lp(c, lp, free, dirty, flags, idx_gc_cnt);
+ if (IS_ERR(lp))
+ err = PTR_ERR(lp);
+
+out:
+ ubifs_release_lprops(c);
+ if (err)
+ ubifs_err(c, "cannot change properties of LEB %d, error %d",
+ lnum, err);
+ return err;
+}
+
+/**
+ * ubifs_update_one_lp - update LEB properties.
+ * @c: the UBIFS file-system description object
+ * @lnum: LEB to change properties for
+ * @free: amount of free space
+ * @dirty: amount of dirty space to add
+ * @flags_set: flags to set
+ * @flags_clean: flags to clean
+ *
+ * This function is the same as 'ubifs_change_one_lp()' but @dirty is added to
+ * current dirty space, not substitutes it.
+ */
+int ubifs_update_one_lp(struct ubifs_info *c, int lnum, int free, int dirty,
+ int flags_set, int flags_clean)
+{
+ int err = 0, flags;
+ const struct ubifs_lprops *lp;
+
+ ubifs_get_lprops(c);
+
+ lp = ubifs_lpt_lookup_dirty(c, lnum);
+ if (IS_ERR(lp)) {
+ err = PTR_ERR(lp);
+ goto out;
+ }
+
+ flags = (lp->flags | flags_set) & ~flags_clean;
+ lp = ubifs_change_lp(c, lp, free, lp->dirty + dirty, flags, 0);
+ if (IS_ERR(lp))
+ err = PTR_ERR(lp);
+
+out:
+ ubifs_release_lprops(c);
+ if (err)
+ ubifs_err(c, "cannot update properties of LEB %d, error %d",
+ lnum, err);
+ return err;
+}
+
+/**
+ * ubifs_read_one_lp - read LEB properties.
+ * @c: the UBIFS file-system description object
+ * @lnum: LEB to read properties for
+ * @lp: where to store read properties
+ *
+ * This helper function reads properties of a LEB @lnum and stores them in @lp.
+ * Returns zero in case of success and a negative error code in case of
+ * failure.
+ */
+int ubifs_read_one_lp(struct ubifs_info *c, int lnum, struct ubifs_lprops *lp)
+{
+ int err = 0;
+ const struct ubifs_lprops *lpp;
+
+ ubifs_get_lprops(c);
+
+ lpp = ubifs_lpt_lookup(c, lnum);
+ if (IS_ERR(lpp)) {
+ err = PTR_ERR(lpp);
+ ubifs_err(c, "cannot read properties of LEB %d, error %d",
+ lnum, err);
+ goto out;
+ }
+
+ memcpy(lp, lpp, sizeof(struct ubifs_lprops));
+
+out:
+ ubifs_release_lprops(c);
+ return err;
+}
+
+/**
+ * ubifs_fast_find_free - try to find a LEB with free space quickly.
+ * @c: the UBIFS file-system description object
+ *
+ * This function returns LEB properties for a LEB with free space or %NULL if
+ * the function is unable to find a LEB quickly.
+ */
+const struct ubifs_lprops *ubifs_fast_find_free(struct ubifs_info *c)
+{
+ struct ubifs_lprops *lprops;
+ struct ubifs_lpt_heap *heap;
+
+ ubifs_assert(mutex_is_locked(&c->lp_mutex));
+
+ heap = &c->lpt_heap[LPROPS_FREE - 1];
+ if (heap->cnt == 0)
+ return NULL;
+
+ lprops = heap->arr[0];
+ ubifs_assert(!(lprops->flags & LPROPS_TAKEN));
+ ubifs_assert(!(lprops->flags & LPROPS_INDEX));
+ return lprops;
+}
+
+/**
+ * ubifs_fast_find_empty - try to find an empty LEB quickly.
+ * @c: the UBIFS file-system description object
+ *
+ * This function returns LEB properties for an empty LEB or %NULL if the
+ * function is unable to find an empty LEB quickly.
+ */
+const struct ubifs_lprops *ubifs_fast_find_empty(struct ubifs_info *c)
+{
+ struct ubifs_lprops *lprops;
+
+ ubifs_assert(mutex_is_locked(&c->lp_mutex));
+
+ if (list_empty(&c->empty_list))
+ return NULL;
+
+ lprops = list_entry(c->empty_list.next, struct ubifs_lprops, list);
+ ubifs_assert(!(lprops->flags & LPROPS_TAKEN));
+ ubifs_assert(!(lprops->flags & LPROPS_INDEX));
+ ubifs_assert(lprops->free == c->leb_size);
+ return lprops;
+}
+
+/**
+ * ubifs_fast_find_freeable - try to find a freeable LEB quickly.
+ * @c: the UBIFS file-system description object
+ *
+ * This function returns LEB properties for a freeable LEB or %NULL if the
+ * function is unable to find a freeable LEB quickly.
+ */
+const struct ubifs_lprops *ubifs_fast_find_freeable(struct ubifs_info *c)
+{
+ struct ubifs_lprops *lprops;
+
+ ubifs_assert(mutex_is_locked(&c->lp_mutex));
+
+ if (list_empty(&c->freeable_list))
+ return NULL;
+
+ lprops = list_entry(c->freeable_list.next, struct ubifs_lprops, list);
+ ubifs_assert(!(lprops->flags & LPROPS_TAKEN));
+ ubifs_assert(!(lprops->flags & LPROPS_INDEX));
+ ubifs_assert(lprops->free + lprops->dirty == c->leb_size);
+ ubifs_assert(c->freeable_cnt > 0);
+ return lprops;
+}
+
+/**
+ * ubifs_fast_find_frdi_idx - try to find a freeable index LEB quickly.
+ * @c: the UBIFS file-system description object
+ *
+ * This function returns LEB properties for a freeable index LEB or %NULL if the
+ * function is unable to find a freeable index LEB quickly.
+ */
+const struct ubifs_lprops *ubifs_fast_find_frdi_idx(struct ubifs_info *c)
+{
+ struct ubifs_lprops *lprops;
+
+ ubifs_assert(mutex_is_locked(&c->lp_mutex));
+
+ if (list_empty(&c->frdi_idx_list))
+ return NULL;
+
+ lprops = list_entry(c->frdi_idx_list.next, struct ubifs_lprops, list);
+ ubifs_assert(!(lprops->flags & LPROPS_TAKEN));
+ ubifs_assert((lprops->flags & LPROPS_INDEX));
+ ubifs_assert(lprops->free + lprops->dirty == c->leb_size);
+ return lprops;
+}
+
+/*
+ * Everything below is related to debugging.
+ */
+
+/**
+ * dbg_check_cats - check category heaps and lists.
+ * @c: UBIFS file-system description object
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int dbg_check_cats(struct ubifs_info *c)
+{
+ struct ubifs_lprops *lprops;
+ struct list_head *pos;
+ int i, cat;
+
+ if (!dbg_is_chk_gen(c) && !dbg_is_chk_lprops(c))
+ return 0;
+
+ list_for_each_entry(lprops, &c->empty_list, list) {
+ if (lprops->free != c->leb_size) {
+ ubifs_err(c, "non-empty LEB %d on empty list (free %d dirty %d flags %d)",
+ lprops->lnum, lprops->free, lprops->dirty,
+ lprops->flags);
+ return -EINVAL;
+ }
+ if (lprops->flags & LPROPS_TAKEN) {
+ ubifs_err(c, "taken LEB %d on empty list (free %d dirty %d flags %d)",
+ lprops->lnum, lprops->free, lprops->dirty,
+ lprops->flags);
+ return -EINVAL;
+ }
+ }
+
+ i = 0;
+ list_for_each_entry(lprops, &c->freeable_list, list) {
+ if (lprops->free + lprops->dirty != c->leb_size) {
+ ubifs_err(c, "non-freeable LEB %d on freeable list (free %d dirty %d flags %d)",
+ lprops->lnum, lprops->free, lprops->dirty,
+ lprops->flags);
+ return -EINVAL;
+ }
+ if (lprops->flags & LPROPS_TAKEN) {
+ ubifs_err(c, "taken LEB %d on freeable list (free %d dirty %d flags %d)",
+ lprops->lnum, lprops->free, lprops->dirty,
+ lprops->flags);
+ return -EINVAL;
+ }
+ i += 1;
+ }
+ if (i != c->freeable_cnt) {
+ ubifs_err(c, "freeable list count %d expected %d", i,
+ c->freeable_cnt);
+ return -EINVAL;
+ }
+
+ i = 0;
+ list_for_each(pos, &c->idx_gc)
+ i += 1;
+ if (i != c->idx_gc_cnt) {
+ ubifs_err(c, "idx_gc list count %d expected %d", i,
+ c->idx_gc_cnt);
+ return -EINVAL;
+ }
+
+ list_for_each_entry(lprops, &c->frdi_idx_list, list) {
+ if (lprops->free + lprops->dirty != c->leb_size) {
+ ubifs_err(c, "non-freeable LEB %d on frdi_idx list (free %d dirty %d flags %d)",
+ lprops->lnum, lprops->free, lprops->dirty,
+ lprops->flags);
+ return -EINVAL;
+ }
+ if (lprops->flags & LPROPS_TAKEN) {
+ ubifs_err(c, "taken LEB %d on frdi_idx list (free %d dirty %d flags %d)",
+ lprops->lnum, lprops->free, lprops->dirty,
+ lprops->flags);
+ return -EINVAL;
+ }
+ if (!(lprops->flags & LPROPS_INDEX)) {
+ ubifs_err(c, "non-index LEB %d on frdi_idx list (free %d dirty %d flags %d)",
+ lprops->lnum, lprops->free, lprops->dirty,
+ lprops->flags);
+ return -EINVAL;
+ }
+ }
+
+ for (cat = 1; cat <= LPROPS_HEAP_CNT; cat++) {
+ struct ubifs_lpt_heap *heap = &c->lpt_heap[cat - 1];
+
+ for (i = 0; i < heap->cnt; i++) {
+ lprops = heap->arr[i];
+ if (!lprops) {
+ ubifs_err(c, "null ptr in LPT heap cat %d", cat);
+ return -EINVAL;
+ }
+ if (lprops->hpos != i) {
+ ubifs_err(c, "bad ptr in LPT heap cat %d", cat);
+ return -EINVAL;
+ }
+ if (lprops->flags & LPROPS_TAKEN) {
+ ubifs_err(c, "taken LEB in LPT heap cat %d", cat);
+ return -EINVAL;
+ }
+ }
+ }
+
+ return 0;
+}
+
+void dbg_check_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap, int cat,
+ int add_pos)
+{
+ int i = 0, j, err = 0;
+
+ if (!dbg_is_chk_gen(c) && !dbg_is_chk_lprops(c))
+ return;
+
+ for (i = 0; i < heap->cnt; i++) {
+ struct ubifs_lprops *lprops = heap->arr[i];
+ struct ubifs_lprops *lp;
+
+ if (i != add_pos)
+ if ((lprops->flags & LPROPS_CAT_MASK) != cat) {
+ err = 1;
+ goto out;
+ }
+ if (lprops->hpos != i) {
+ err = 2;
+ goto out;
+ }
+ lp = ubifs_lpt_lookup(c, lprops->lnum);
+ if (IS_ERR(lp)) {
+ err = 3;
+ goto out;
+ }
+ if (lprops != lp) {
+ ubifs_err(c, "lprops %zx lp %zx lprops->lnum %d lp->lnum %d",
+ (size_t)lprops, (size_t)lp, lprops->lnum,
+ lp->lnum);
+ err = 4;
+ goto out;
+ }
+ for (j = 0; j < i; j++) {
+ lp = heap->arr[j];
+ if (lp == lprops) {
+ err = 5;
+ goto out;
+ }
+ if (lp->lnum == lprops->lnum) {
+ err = 6;
+ goto out;
+ }
+ }
+ }
+out:
+ if (err) {
+ ubifs_err(c, "failed cat %d hpos %d err %d", cat, i, err);
+ dump_stack();
+ ubifs_dump_heap(c, heap, cat);
+ }
+}
+
+/**
+ * scan_check_cb - scan callback.
+ * @c: the UBIFS file-system description object
+ * @lp: LEB properties to scan
+ * @in_tree: whether the LEB properties are in main memory
+ * @lst: lprops statistics to update
+ *
+ * This function returns a code that indicates whether the scan should continue
+ * (%LPT_SCAN_CONTINUE), whether the LEB properties should be added to the tree
+ * in main memory (%LPT_SCAN_ADD), or whether the scan should stop
+ * (%LPT_SCAN_STOP).
+ */
+static int scan_check_cb(struct ubifs_info *c,
+ const struct ubifs_lprops *lp, int in_tree,
+ struct ubifs_lp_stats *lst)
+{
+ struct ubifs_scan_leb *sleb;
+ struct ubifs_scan_node *snod;
+ int cat, lnum = lp->lnum, is_idx = 0, used = 0, free, dirty, ret;
+ void *buf = NULL;
+
+ cat = lp->flags & LPROPS_CAT_MASK;
+ if (cat != LPROPS_UNCAT) {
+ cat = ubifs_categorize_lprops(c, lp);
+ if (cat != (lp->flags & LPROPS_CAT_MASK)) {
+ ubifs_err(c, "bad LEB category %d expected %d",
+ (lp->flags & LPROPS_CAT_MASK), cat);
+ return -EINVAL;
+ }
+ }
+
+ /* Check lp is on its category list (if it has one) */
+ if (in_tree) {
+ struct list_head *list = NULL;
+
+ switch (cat) {
+ case LPROPS_EMPTY:
+ list = &c->empty_list;
+ break;
+ case LPROPS_FREEABLE:
+ list = &c->freeable_list;
+ break;
+ case LPROPS_FRDI_IDX:
+ list = &c->frdi_idx_list;
+ break;
+ case LPROPS_UNCAT:
+ list = &c->uncat_list;
+ break;
+ }
+ if (list) {
+ struct ubifs_lprops *lprops;
+ int found = 0;
+
+ list_for_each_entry(lprops, list, list) {
+ if (lprops == lp) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ ubifs_err(c, "bad LPT list (category %d)", cat);
+ return -EINVAL;
+ }
+ }
+ }
+
+ /* Check lp is on its category heap (if it has one) */
+ if (in_tree && cat > 0 && cat <= LPROPS_HEAP_CNT) {
+ struct ubifs_lpt_heap *heap = &c->lpt_heap[cat - 1];
+
+ if ((lp->hpos != -1 && heap->arr[lp->hpos]->lnum != lnum) ||
+ lp != heap->arr[lp->hpos]) {
+ ubifs_err(c, "bad LPT heap (category %d)", cat);
+ return -EINVAL;
+ }
+ }
+
+ buf = __vmalloc(c->leb_size, GFP_NOFS, PAGE_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ /*
+ * After an unclean unmount, empty and freeable LEBs
+ * may contain garbage - do not scan them.
+ */
+ if (lp->free == c->leb_size) {
+ lst->empty_lebs += 1;
+ lst->total_free += c->leb_size;
+ lst->total_dark += ubifs_calc_dark(c, c->leb_size);
+ return LPT_SCAN_CONTINUE;
+ }
+ if (lp->free + lp->dirty == c->leb_size &&
+ !(lp->flags & LPROPS_INDEX)) {
+ lst->total_free += lp->free;
+ lst->total_dirty += lp->dirty;
+ lst->total_dark += ubifs_calc_dark(c, c->leb_size);
+ return LPT_SCAN_CONTINUE;
+ }
+
+ sleb = ubifs_scan(c, lnum, 0, buf, 0);
+ if (IS_ERR(sleb)) {
+ ret = PTR_ERR(sleb);
+ if (ret == -EUCLEAN) {
+ ubifs_dump_lprops(c);
+ ubifs_dump_budg(c, &c->bi);
+ }
+ goto out;
+ }
+
+ is_idx = -1;
+ list_for_each_entry(snod, &sleb->nodes, list) {
+ int found, level = 0;
+
+ cond_resched();
+
+ if (is_idx == -1)
+ is_idx = (snod->type == UBIFS_IDX_NODE) ? 1 : 0;
+
+ if (is_idx && snod->type != UBIFS_IDX_NODE) {
+ ubifs_err(c, "indexing node in data LEB %d:%d",
+ lnum, snod->offs);
+ goto out_destroy;
+ }
+
+ if (snod->type == UBIFS_IDX_NODE) {
+ struct ubifs_idx_node *idx = snod->node;
+
+ key_read(c, ubifs_idx_key(c, idx), &snod->key);
+ level = le16_to_cpu(idx->level);
+ }
+
+ found = ubifs_tnc_has_node(c, &snod->key, level, lnum,
+ snod->offs, is_idx);
+ if (found) {
+ if (found < 0)
+ goto out_destroy;
+ used += ALIGN(snod->len, 8);
+ }
+ }
+
+ free = c->leb_size - sleb->endpt;
+ dirty = sleb->endpt - used;
+
+ if (free > c->leb_size || free < 0 || dirty > c->leb_size ||
+ dirty < 0) {
+ ubifs_err(c, "bad calculated accounting for LEB %d: free %d, dirty %d",
+ lnum, free, dirty);
+ goto out_destroy;
+ }
+
+ if (lp->free + lp->dirty == c->leb_size &&
+ free + dirty == c->leb_size)
+ if ((is_idx && !(lp->flags & LPROPS_INDEX)) ||
+ (!is_idx && free == c->leb_size) ||
+ lp->free == c->leb_size) {
+ /*
+ * Empty or freeable LEBs could contain index
+ * nodes from an uncompleted commit due to an
+ * unclean unmount. Or they could be empty for
+ * the same reason. Or it may simply not have been
+ * unmapped.
+ */
+ free = lp->free;
+ dirty = lp->dirty;
+ is_idx = 0;
+ }
+
+ if (is_idx && lp->free + lp->dirty == free + dirty &&
+ lnum != c->ihead_lnum) {
+ /*
+ * After an unclean unmount, an index LEB could have a different
+ * amount of free space than the value recorded by lprops. That
+ * is because the in-the-gaps method may use free space or
+ * create free space (as a side-effect of using ubi_leb_change
+ * and not writing the whole LEB). The incorrect free space
+ * value is not a problem because the index is only ever
+ * allocated empty LEBs, so there will never be an attempt to
+ * write to the free space at the end of an index LEB - except
+ * by the in-the-gaps method for which it is not a problem.
+ */
+ free = lp->free;
+ dirty = lp->dirty;
+ }
+
+ if (lp->free != free || lp->dirty != dirty)
+ goto out_print;
+
+ if (is_idx && !(lp->flags & LPROPS_INDEX)) {
+ if (free == c->leb_size)
+ /* Free but not unmapped LEB, it's fine */
+ is_idx = 0;
+ else {
+ ubifs_err(c, "indexing node without indexing flag");
+ goto out_print;
+ }
+ }
+
+ if (!is_idx && (lp->flags & LPROPS_INDEX)) {
+ ubifs_err(c, "data node with indexing flag");
+ goto out_print;
+ }
+
+ if (free == c->leb_size)
+ lst->empty_lebs += 1;
+
+ if (is_idx)
+ lst->idx_lebs += 1;
+
+ if (!(lp->flags & LPROPS_INDEX))
+ lst->total_used += c->leb_size - free - dirty;
+ lst->total_free += free;
+ lst->total_dirty += dirty;
+
+ if (!(lp->flags & LPROPS_INDEX)) {
+ int spc = free + dirty;
+
+ if (spc < c->dead_wm)
+ lst->total_dead += spc;
+ else
+ lst->total_dark += ubifs_calc_dark(c, spc);
+ }
+
+ ubifs_scan_destroy(sleb);
+ vfree(buf);
+ return LPT_SCAN_CONTINUE;
+
+out_print:
+ ubifs_err(c, "bad accounting of LEB %d: free %d, dirty %d flags %#x, should be free %d, dirty %d",
+ lnum, lp->free, lp->dirty, lp->flags, free, dirty);
+ ubifs_dump_leb(c, lnum);
+out_destroy:
+ ubifs_scan_destroy(sleb);
+ ret = -EINVAL;
+out:
+ vfree(buf);
+ return ret;
+}
+
+/**
+ * dbg_check_lprops - check all LEB properties.
+ * @c: UBIFS file-system description object
+ *
+ * This function checks all LEB properties and makes sure they are all correct.
+ * It returns zero if everything is fine, %-EINVAL if there is an inconsistency
+ * and other negative error codes in case of other errors. This function is
+ * called while the file system is locked (because of commit start), so no
+ * additional locking is required. Note that locking the LPT mutex would cause
+ * a circular lock dependency with the TNC mutex.
+ */
+int dbg_check_lprops(struct ubifs_info *c)
+{
+ int i, err;
+ struct ubifs_lp_stats lst;
+
+ if (!dbg_is_chk_lprops(c))
+ return 0;
+
+ /*
+ * As we are going to scan the media, the write buffers have to be
+ * synchronized.
+ */
+ for (i = 0; i < c->jhead_cnt; i++) {
+ err = ubifs_wbuf_sync(&c->jheads[i].wbuf);
+ if (err)
+ return err;
+ }
+
+ memset(&lst, 0, sizeof(struct ubifs_lp_stats));
+ err = ubifs_lpt_scan_nolock(c, c->main_first, c->leb_cnt - 1,
+ (ubifs_lpt_scan_callback)scan_check_cb,
+ &lst);
+ if (err && err != -ENOSPC)
+ goto out;
+
+ if (lst.empty_lebs != c->lst.empty_lebs ||
+ lst.idx_lebs != c->lst.idx_lebs ||
+ lst.total_free != c->lst.total_free ||
+ lst.total_dirty != c->lst.total_dirty ||
+ lst.total_used != c->lst.total_used) {
+ ubifs_err(c, "bad overall accounting");
+ ubifs_err(c, "calculated: empty_lebs %d, idx_lebs %d, total_free %lld, total_dirty %lld, total_used %lld",
+ lst.empty_lebs, lst.idx_lebs, lst.total_free,
+ lst.total_dirty, lst.total_used);
+ ubifs_err(c, "read from lprops: empty_lebs %d, idx_lebs %d, total_free %lld, total_dirty %lld, total_used %lld",
+ c->lst.empty_lebs, c->lst.idx_lebs, c->lst.total_free,
+ c->lst.total_dirty, c->lst.total_used);
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (lst.total_dead != c->lst.total_dead ||
+ lst.total_dark != c->lst.total_dark) {
+ ubifs_err(c, "bad dead/dark space accounting");
+ ubifs_err(c, "calculated: total_dead %lld, total_dark %lld",
+ lst.total_dead, lst.total_dark);
+ ubifs_err(c, "read from lprops: total_dead %lld, total_dark %lld",
+ c->lst.total_dead, c->lst.total_dark);
+ err = -EINVAL;
+ goto out;
+ }
+
+ err = dbg_check_cats(c);
+out:
+ return err;
+}
diff --git a/roms/u-boot/fs/ubifs/lpt.c b/roms/u-boot/fs/ubifs/lpt.c
new file mode 100644
index 000000000..62748b021
--- /dev/null
+++ b/roms/u-boot/fs/ubifs/lpt.c
@@ -0,0 +1,2285 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ *
+ * Authors: Adrian Hunter
+ * Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * This file implements the LEB properties tree (LPT) area. The LPT area
+ * contains the LEB properties tree, a table of LPT area eraseblocks (ltab), and
+ * (for the "big" model) a table of saved LEB numbers (lsave). The LPT area sits
+ * between the log and the orphan area.
+ *
+ * The LPT area is like a miniature self-contained file system. It is required
+ * that it never runs out of space, is fast to access and update, and scales
+ * logarithmically. The LEB properties tree is implemented as a wandering tree
+ * much like the TNC, and the LPT area has its own garbage collection.
+ *
+ * The LPT has two slightly different forms called the "small model" and the
+ * "big model". The small model is used when the entire LEB properties table
+ * can be written into a single eraseblock. In that case, garbage collection
+ * consists of just writing the whole table, which therefore makes all other
+ * eraseblocks reusable. In the case of the big model, dirty eraseblocks are
+ * selected for garbage collection, which consists of marking the clean nodes in
+ * that LEB as dirty, and then only the dirty nodes are written out. Also, in
+ * the case of the big model, a table of LEB numbers is saved so that the entire
+ * LPT does not to be scanned looking for empty eraseblocks when UBIFS is first
+ * mounted.
+ */
+
+#include "ubifs.h"
+#ifndef __UBOOT__
+#include <log.h>
+#include <dm/devres.h>
+#include <linux/crc16.h>
+#include <linux/math64.h>
+#include <linux/slab.h>
+#else
+#include <linux/compat.h>
+#include <linux/err.h>
+#include <ubi_uboot.h>
+#include "crc16.h"
+#endif
+
+/**
+ * do_calc_lpt_geom - calculate sizes for the LPT area.
+ * @c: the UBIFS file-system description object
+ *
+ * Calculate the sizes of LPT bit fields, nodes, and tree, based on the
+ * properties of the flash and whether LPT is "big" (c->big_lpt).
+ */
+static void do_calc_lpt_geom(struct ubifs_info *c)
+{
+ int i, n, bits, per_leb_wastage, max_pnode_cnt;
+ long long sz, tot_wastage;
+
+ n = c->main_lebs + c->max_leb_cnt - c->leb_cnt;
+ max_pnode_cnt = DIV_ROUND_UP(n, UBIFS_LPT_FANOUT);
+
+ c->lpt_hght = 1;
+ n = UBIFS_LPT_FANOUT;
+ while (n < max_pnode_cnt) {
+ c->lpt_hght += 1;
+ n <<= UBIFS_LPT_FANOUT_SHIFT;
+ }
+
+ c->pnode_cnt = DIV_ROUND_UP(c->main_lebs, UBIFS_LPT_FANOUT);
+
+ n = DIV_ROUND_UP(c->pnode_cnt, UBIFS_LPT_FANOUT);
+ c->nnode_cnt = n;
+ for (i = 1; i < c->lpt_hght; i++) {
+ n = DIV_ROUND_UP(n, UBIFS_LPT_FANOUT);
+ c->nnode_cnt += n;
+ }
+
+ c->space_bits = fls(c->leb_size) - 3;
+ c->lpt_lnum_bits = fls(c->lpt_lebs);
+ c->lpt_offs_bits = fls(c->leb_size - 1);
+ c->lpt_spc_bits = fls(c->leb_size);
+
+ n = DIV_ROUND_UP(c->max_leb_cnt, UBIFS_LPT_FANOUT);
+ c->pcnt_bits = fls(n - 1);
+
+ c->lnum_bits = fls(c->max_leb_cnt - 1);
+
+ bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS +
+ (c->big_lpt ? c->pcnt_bits : 0) +
+ (c->space_bits * 2 + 1) * UBIFS_LPT_FANOUT;
+ c->pnode_sz = (bits + 7) / 8;
+
+ bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS +
+ (c->big_lpt ? c->pcnt_bits : 0) +
+ (c->lpt_lnum_bits + c->lpt_offs_bits) * UBIFS_LPT_FANOUT;
+ c->nnode_sz = (bits + 7) / 8;
+
+ bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS +
+ c->lpt_lebs * c->lpt_spc_bits * 2;
+ c->ltab_sz = (bits + 7) / 8;
+
+ bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS +
+ c->lnum_bits * c->lsave_cnt;
+ c->lsave_sz = (bits + 7) / 8;
+
+ /* Calculate the minimum LPT size */
+ c->lpt_sz = (long long)c->pnode_cnt * c->pnode_sz;
+ c->lpt_sz += (long long)c->nnode_cnt * c->nnode_sz;
+ c->lpt_sz += c->ltab_sz;
+ if (c->big_lpt)
+ c->lpt_sz += c->lsave_sz;
+
+ /* Add wastage */
+ sz = c->lpt_sz;
+ per_leb_wastage = max_t(int, c->pnode_sz, c->nnode_sz);
+ sz += per_leb_wastage;
+ tot_wastage = per_leb_wastage;
+ while (sz > c->leb_size) {
+ sz += per_leb_wastage;
+ sz -= c->leb_size;
+ tot_wastage += per_leb_wastage;
+ }
+ tot_wastage += ALIGN(sz, c->min_io_size) - sz;
+ c->lpt_sz += tot_wastage;
+}
+
+/**
+ * ubifs_calc_lpt_geom - calculate and check sizes for the LPT area.
+ * @c: the UBIFS file-system description object
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int ubifs_calc_lpt_geom(struct ubifs_info *c)
+{
+ int lebs_needed;
+ long long sz;
+
+ do_calc_lpt_geom(c);
+
+ /* Verify that lpt_lebs is big enough */
+ sz = c->lpt_sz * 2; /* Must have at least 2 times the size */
+ lebs_needed = div_u64(sz + c->leb_size - 1, c->leb_size);
+ if (lebs_needed > c->lpt_lebs) {
+ ubifs_err(c, "too few LPT LEBs");
+ return -EINVAL;
+ }
+
+ /* Verify that ltab fits in a single LEB (since ltab is a single node */
+ if (c->ltab_sz > c->leb_size) {
+ ubifs_err(c, "LPT ltab too big");
+ return -EINVAL;
+ }
+
+ c->check_lpt_free = c->big_lpt;
+ return 0;
+}
+
+/**
+ * calc_dflt_lpt_geom - calculate default LPT geometry.
+ * @c: the UBIFS file-system description object
+ * @main_lebs: number of main area LEBs is passed and returned here
+ * @big_lpt: whether the LPT area is "big" is returned here
+ *
+ * The size of the LPT area depends on parameters that themselves are dependent
+ * on the size of the LPT area. This function, successively recalculates the LPT
+ * area geometry until the parameters and resultant geometry are consistent.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int calc_dflt_lpt_geom(struct ubifs_info *c, int *main_lebs,
+ int *big_lpt)
+{
+ int i, lebs_needed;
+ long long sz;
+
+ /* Start by assuming the minimum number of LPT LEBs */
+ c->lpt_lebs = UBIFS_MIN_LPT_LEBS;
+ c->main_lebs = *main_lebs - c->lpt_lebs;
+ if (c->main_lebs <= 0)
+ return -EINVAL;
+
+ /* And assume we will use the small LPT model */
+ c->big_lpt = 0;
+
+ /*
+ * Calculate the geometry based on assumptions above and then see if it
+ * makes sense
+ */
+ do_calc_lpt_geom(c);
+
+ /* Small LPT model must have lpt_sz < leb_size */
+ if (c->lpt_sz > c->leb_size) {
+ /* Nope, so try again using big LPT model */
+ c->big_lpt = 1;
+ do_calc_lpt_geom(c);
+ }
+
+ /* Now check there are enough LPT LEBs */
+ for (i = 0; i < 64 ; i++) {
+ sz = c->lpt_sz * 4; /* Allow 4 times the size */
+ lebs_needed = div_u64(sz + c->leb_size - 1, c->leb_size);
+ if (lebs_needed > c->lpt_lebs) {
+ /* Not enough LPT LEBs so try again with more */
+ c->lpt_lebs = lebs_needed;
+ c->main_lebs = *main_lebs - c->lpt_lebs;
+ if (c->main_lebs <= 0)
+ return -EINVAL;
+ do_calc_lpt_geom(c);
+ continue;
+ }
+ if (c->ltab_sz > c->leb_size) {
+ ubifs_err(c, "LPT ltab too big");
+ return -EINVAL;
+ }
+ *main_lebs = c->main_lebs;
+ *big_lpt = c->big_lpt;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+/**
+ * pack_bits - pack bit fields end-to-end.
+ * @addr: address at which to pack (passed and next address returned)
+ * @pos: bit position at which to pack (passed and next position returned)
+ * @val: value to pack
+ * @nrbits: number of bits of value to pack (1-32)
+ */
+static void pack_bits(uint8_t **addr, int *pos, uint32_t val, int nrbits)
+{
+ uint8_t *p = *addr;
+ int b = *pos;
+
+ ubifs_assert(nrbits > 0);
+ ubifs_assert(nrbits <= 32);
+ ubifs_assert(*pos >= 0);
+ ubifs_assert(*pos < 8);
+ ubifs_assert((val >> nrbits) == 0 || nrbits == 32);
+ if (b) {
+ *p |= ((uint8_t)val) << b;
+ nrbits += b;
+ if (nrbits > 8) {
+ *++p = (uint8_t)(val >>= (8 - b));
+ if (nrbits > 16) {
+ *++p = (uint8_t)(val >>= 8);
+ if (nrbits > 24) {
+ *++p = (uint8_t)(val >>= 8);
+ if (nrbits > 32)
+ *++p = (uint8_t)(val >>= 8);
+ }
+ }
+ }
+ } else {
+ *p = (uint8_t)val;
+ if (nrbits > 8) {
+ *++p = (uint8_t)(val >>= 8);
+ if (nrbits > 16) {
+ *++p = (uint8_t)(val >>= 8);
+ if (nrbits > 24)
+ *++p = (uint8_t)(val >>= 8);
+ }
+ }
+ }
+ b = nrbits & 7;
+ if (b == 0)
+ p++;
+ *addr = p;
+ *pos = b;
+}
+
+/**
+ * ubifs_unpack_bits - unpack bit fields.
+ * @addr: address at which to unpack (passed and next address returned)
+ * @pos: bit position at which to unpack (passed and next position returned)
+ * @nrbits: number of bits of value to unpack (1-32)
+ *
+ * This functions returns the value unpacked.
+ */
+uint32_t ubifs_unpack_bits(uint8_t **addr, int *pos, int nrbits)
+{
+ const int k = 32 - nrbits;
+ uint8_t *p = *addr;
+ int b = *pos;
+ uint32_t uninitialized_var(val);
+ const int bytes = (nrbits + b + 7) >> 3;
+
+ ubifs_assert(nrbits > 0);
+ ubifs_assert(nrbits <= 32);
+ ubifs_assert(*pos >= 0);
+ ubifs_assert(*pos < 8);
+ if (b) {
+ switch (bytes) {
+ case 2:
+ val = p[1];
+ break;
+ case 3:
+ val = p[1] | ((uint32_t)p[2] << 8);
+ break;
+ case 4:
+ val = p[1] | ((uint32_t)p[2] << 8) |
+ ((uint32_t)p[3] << 16);
+ break;
+ case 5:
+ val = p[1] | ((uint32_t)p[2] << 8) |
+ ((uint32_t)p[3] << 16) |
+ ((uint32_t)p[4] << 24);
+ }
+ val <<= (8 - b);
+ val |= *p >> b;
+ nrbits += b;
+ } else {
+ switch (bytes) {
+ case 1:
+ val = p[0];
+ break;
+ case 2:
+ val = p[0] | ((uint32_t)p[1] << 8);
+ break;
+ case 3:
+ val = p[0] | ((uint32_t)p[1] << 8) |
+ ((uint32_t)p[2] << 16);
+ break;
+ case 4:
+ val = p[0] | ((uint32_t)p[1] << 8) |
+ ((uint32_t)p[2] << 16) |
+ ((uint32_t)p[3] << 24);
+ break;
+ }
+ }
+ val <<= k;
+ val >>= k;
+ b = nrbits & 7;
+ p += nrbits >> 3;
+ *addr = p;
+ *pos = b;
+ ubifs_assert((val >> nrbits) == 0 || nrbits - b == 32);
+ return val;
+}
+
+/**
+ * ubifs_pack_pnode - pack all the bit fields of a pnode.
+ * @c: UBIFS file-system description object
+ * @buf: buffer into which to pack
+ * @pnode: pnode to pack
+ */
+void ubifs_pack_pnode(struct ubifs_info *c, void *buf,
+ struct ubifs_pnode *pnode)
+{
+ uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES;
+ int i, pos = 0;
+ uint16_t crc;
+
+ pack_bits(&addr, &pos, UBIFS_LPT_PNODE, UBIFS_LPT_TYPE_BITS);
+ if (c->big_lpt)
+ pack_bits(&addr, &pos, pnode->num, c->pcnt_bits);
+ for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
+ pack_bits(&addr, &pos, pnode->lprops[i].free >> 3,
+ c->space_bits);
+ pack_bits(&addr, &pos, pnode->lprops[i].dirty >> 3,
+ c->space_bits);
+ if (pnode->lprops[i].flags & LPROPS_INDEX)
+ pack_bits(&addr, &pos, 1, 1);
+ else
+ pack_bits(&addr, &pos, 0, 1);
+ }
+ crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES,
+ c->pnode_sz - UBIFS_LPT_CRC_BYTES);
+ addr = buf;
+ pos = 0;
+ pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS);
+}
+
+/**
+ * ubifs_pack_nnode - pack all the bit fields of a nnode.
+ * @c: UBIFS file-system description object
+ * @buf: buffer into which to pack
+ * @nnode: nnode to pack
+ */
+void ubifs_pack_nnode(struct ubifs_info *c, void *buf,
+ struct ubifs_nnode *nnode)
+{
+ uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES;
+ int i, pos = 0;
+ uint16_t crc;
+
+ pack_bits(&addr, &pos, UBIFS_LPT_NNODE, UBIFS_LPT_TYPE_BITS);
+ if (c->big_lpt)
+ pack_bits(&addr, &pos, nnode->num, c->pcnt_bits);
+ for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
+ int lnum = nnode->nbranch[i].lnum;
+
+ if (lnum == 0)
+ lnum = c->lpt_last + 1;
+ pack_bits(&addr, &pos, lnum - c->lpt_first, c->lpt_lnum_bits);
+ pack_bits(&addr, &pos, nnode->nbranch[i].offs,
+ c->lpt_offs_bits);
+ }
+ crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES,
+ c->nnode_sz - UBIFS_LPT_CRC_BYTES);
+ addr = buf;
+ pos = 0;
+ pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS);
+}
+
+/**
+ * ubifs_pack_ltab - pack the LPT's own lprops table.
+ * @c: UBIFS file-system description object
+ * @buf: buffer into which to pack
+ * @ltab: LPT's own lprops table to pack
+ */
+void ubifs_pack_ltab(struct ubifs_info *c, void *buf,
+ struct ubifs_lpt_lprops *ltab)
+{
+ uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES;
+ int i, pos = 0;
+ uint16_t crc;
+
+ pack_bits(&addr, &pos, UBIFS_LPT_LTAB, UBIFS_LPT_TYPE_BITS);
+ for (i = 0; i < c->lpt_lebs; i++) {
+ pack_bits(&addr, &pos, ltab[i].free, c->lpt_spc_bits);
+ pack_bits(&addr, &pos, ltab[i].dirty, c->lpt_spc_bits);
+ }
+ crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES,
+ c->ltab_sz - UBIFS_LPT_CRC_BYTES);
+ addr = buf;
+ pos = 0;
+ pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS);
+}
+
+/**
+ * ubifs_pack_lsave - pack the LPT's save table.
+ * @c: UBIFS file-system description object
+ * @buf: buffer into which to pack
+ * @lsave: LPT's save table to pack
+ */
+void ubifs_pack_lsave(struct ubifs_info *c, void *buf, int *lsave)
+{
+ uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES;
+ int i, pos = 0;
+ uint16_t crc;
+
+ pack_bits(&addr, &pos, UBIFS_LPT_LSAVE, UBIFS_LPT_TYPE_BITS);
+ for (i = 0; i < c->lsave_cnt; i++)
+ pack_bits(&addr, &pos, lsave[i], c->lnum_bits);
+ crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES,
+ c->lsave_sz - UBIFS_LPT_CRC_BYTES);
+ addr = buf;
+ pos = 0;
+ pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS);
+}
+
+/**
+ * ubifs_add_lpt_dirt - add dirty space to LPT LEB properties.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number to which to add dirty space
+ * @dirty: amount of dirty space to add
+ */
+void ubifs_add_lpt_dirt(struct ubifs_info *c, int lnum, int dirty)
+{
+ if (!dirty || !lnum)
+ return;
+ dbg_lp("LEB %d add %d to %d",
+ lnum, dirty, c->ltab[lnum - c->lpt_first].dirty);
+ ubifs_assert(lnum >= c->lpt_first && lnum <= c->lpt_last);
+ c->ltab[lnum - c->lpt_first].dirty += dirty;
+}
+
+/**
+ * set_ltab - set LPT LEB properties.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number
+ * @free: amount of free space
+ * @dirty: amount of dirty space
+ */
+static void set_ltab(struct ubifs_info *c, int lnum, int free, int dirty)
+{
+ dbg_lp("LEB %d free %d dirty %d to %d %d",
+ lnum, c->ltab[lnum - c->lpt_first].free,
+ c->ltab[lnum - c->lpt_first].dirty, free, dirty);
+ ubifs_assert(lnum >= c->lpt_first && lnum <= c->lpt_last);
+ c->ltab[lnum - c->lpt_first].free = free;
+ c->ltab[lnum - c->lpt_first].dirty = dirty;
+}
+
+/**
+ * ubifs_add_nnode_dirt - add dirty space to LPT LEB properties.
+ * @c: UBIFS file-system description object
+ * @nnode: nnode for which to add dirt
+ */
+void ubifs_add_nnode_dirt(struct ubifs_info *c, struct ubifs_nnode *nnode)
+{
+ struct ubifs_nnode *np = nnode->parent;
+
+ if (np)
+ ubifs_add_lpt_dirt(c, np->nbranch[nnode->iip].lnum,
+ c->nnode_sz);
+ else {
+ ubifs_add_lpt_dirt(c, c->lpt_lnum, c->nnode_sz);
+ if (!(c->lpt_drty_flgs & LTAB_DIRTY)) {
+ c->lpt_drty_flgs |= LTAB_DIRTY;
+ ubifs_add_lpt_dirt(c, c->ltab_lnum, c->ltab_sz);
+ }
+ }
+}
+
+/**
+ * add_pnode_dirt - add dirty space to LPT LEB properties.
+ * @c: UBIFS file-system description object
+ * @pnode: pnode for which to add dirt
+ */
+static void add_pnode_dirt(struct ubifs_info *c, struct ubifs_pnode *pnode)
+{
+ ubifs_add_lpt_dirt(c, pnode->parent->nbranch[pnode->iip].lnum,
+ c->pnode_sz);
+}
+
+/**
+ * calc_nnode_num - calculate nnode number.
+ * @row: the row in the tree (root is zero)
+ * @col: the column in the row (leftmost is zero)
+ *
+ * The nnode number is a number that uniquely identifies a nnode and can be used
+ * easily to traverse the tree from the root to that nnode.
+ *
+ * This function calculates and returns the nnode number for the nnode at @row
+ * and @col.
+ */
+static int calc_nnode_num(int row, int col)
+{
+ int num, bits;
+
+ num = 1;
+ while (row--) {
+ bits = (col & (UBIFS_LPT_FANOUT - 1));
+ col >>= UBIFS_LPT_FANOUT_SHIFT;
+ num <<= UBIFS_LPT_FANOUT_SHIFT;
+ num |= bits;
+ }
+ return num;
+}
+
+/**
+ * calc_nnode_num_from_parent - calculate nnode number.
+ * @c: UBIFS file-system description object
+ * @parent: parent nnode
+ * @iip: index in parent
+ *
+ * The nnode number is a number that uniquely identifies a nnode and can be used
+ * easily to traverse the tree from the root to that nnode.
+ *
+ * This function calculates and returns the nnode number based on the parent's
+ * nnode number and the index in parent.
+ */
+static int calc_nnode_num_from_parent(const struct ubifs_info *c,
+ struct ubifs_nnode *parent, int iip)
+{
+ int num, shft;
+
+ if (!parent)
+ return 1;
+ shft = (c->lpt_hght - parent->level) * UBIFS_LPT_FANOUT_SHIFT;
+ num = parent->num ^ (1 << shft);
+ num |= (UBIFS_LPT_FANOUT + iip) << shft;
+ return num;
+}
+
+/**
+ * calc_pnode_num_from_parent - calculate pnode number.
+ * @c: UBIFS file-system description object
+ * @parent: parent nnode
+ * @iip: index in parent
+ *
+ * The pnode number is a number that uniquely identifies a pnode and can be used
+ * easily to traverse the tree from the root to that pnode.
+ *
+ * This function calculates and returns the pnode number based on the parent's
+ * nnode number and the index in parent.
+ */
+static int calc_pnode_num_from_parent(const struct ubifs_info *c,
+ struct ubifs_nnode *parent, int iip)
+{
+ int i, n = c->lpt_hght - 1, pnum = parent->num, num = 0;
+
+ for (i = 0; i < n; i++) {
+ num <<= UBIFS_LPT_FANOUT_SHIFT;
+ num |= pnum & (UBIFS_LPT_FANOUT - 1);
+ pnum >>= UBIFS_LPT_FANOUT_SHIFT;
+ }
+ num <<= UBIFS_LPT_FANOUT_SHIFT;
+ num |= iip;
+ return num;
+}
+
+/**
+ * ubifs_create_dflt_lpt - create default LPT.
+ * @c: UBIFS file-system description object
+ * @main_lebs: number of main area LEBs is passed and returned here
+ * @lpt_first: LEB number of first LPT LEB
+ * @lpt_lebs: number of LEBs for LPT is passed and returned here
+ * @big_lpt: use big LPT model is passed and returned here
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int ubifs_create_dflt_lpt(struct ubifs_info *c, int *main_lebs, int lpt_first,
+ int *lpt_lebs, int *big_lpt)
+{
+ int lnum, err = 0, node_sz, iopos, i, j, cnt, len, alen, row;
+ int blnum, boffs, bsz, bcnt;
+ struct ubifs_pnode *pnode = NULL;
+ struct ubifs_nnode *nnode = NULL;
+ void *buf = NULL, *p;
+ struct ubifs_lpt_lprops *ltab = NULL;
+ int *lsave = NULL;
+
+ err = calc_dflt_lpt_geom(c, main_lebs, big_lpt);
+ if (err)
+ return err;
+ *lpt_lebs = c->lpt_lebs;
+
+ /* Needed by 'ubifs_pack_nnode()' and 'set_ltab()' */
+ c->lpt_first = lpt_first;
+ /* Needed by 'set_ltab()' */
+ c->lpt_last = lpt_first + c->lpt_lebs - 1;
+ /* Needed by 'ubifs_pack_lsave()' */
+ c->main_first = c->leb_cnt - *main_lebs;
+
+ lsave = kmalloc(sizeof(int) * c->lsave_cnt, GFP_KERNEL);
+ pnode = kzalloc(sizeof(struct ubifs_pnode), GFP_KERNEL);
+ nnode = kzalloc(sizeof(struct ubifs_nnode), GFP_KERNEL);
+ buf = vmalloc(c->leb_size);
+ ltab = vmalloc(sizeof(struct ubifs_lpt_lprops) * c->lpt_lebs);
+ if (!pnode || !nnode || !buf || !ltab || !lsave) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ ubifs_assert(!c->ltab);
+ c->ltab = ltab; /* Needed by set_ltab */
+
+ /* Initialize LPT's own lprops */
+ for (i = 0; i < c->lpt_lebs; i++) {
+ ltab[i].free = c->leb_size;
+ ltab[i].dirty = 0;
+ ltab[i].tgc = 0;
+ ltab[i].cmt = 0;
+ }
+
+ lnum = lpt_first;
+ p = buf;
+ /* Number of leaf nodes (pnodes) */
+ cnt = c->pnode_cnt;
+
+ /*
+ * The first pnode contains the LEB properties for the LEBs that contain
+ * the root inode node and the root index node of the index tree.
+ */
+ node_sz = ALIGN(ubifs_idx_node_sz(c, 1), 8);
+ iopos = ALIGN(node_sz, c->min_io_size);
+ pnode->lprops[0].free = c->leb_size - iopos;
+ pnode->lprops[0].dirty = iopos - node_sz;
+ pnode->lprops[0].flags = LPROPS_INDEX;
+
+ node_sz = UBIFS_INO_NODE_SZ;
+ iopos = ALIGN(node_sz, c->min_io_size);
+ pnode->lprops[1].free = c->leb_size - iopos;
+ pnode->lprops[1].dirty = iopos - node_sz;
+
+ for (i = 2; i < UBIFS_LPT_FANOUT; i++)
+ pnode->lprops[i].free = c->leb_size;
+
+ /* Add first pnode */
+ ubifs_pack_pnode(c, p, pnode);
+ p += c->pnode_sz;
+ len = c->pnode_sz;
+ pnode->num += 1;
+
+ /* Reset pnode values for remaining pnodes */
+ pnode->lprops[0].free = c->leb_size;
+ pnode->lprops[0].dirty = 0;
+ pnode->lprops[0].flags = 0;
+
+ pnode->lprops[1].free = c->leb_size;
+ pnode->lprops[1].dirty = 0;
+
+ /*
+ * To calculate the internal node branches, we keep information about
+ * the level below.
+ */
+ blnum = lnum; /* LEB number of level below */
+ boffs = 0; /* Offset of level below */
+ bcnt = cnt; /* Number of nodes in level below */
+ bsz = c->pnode_sz; /* Size of nodes in level below */
+
+ /* Add all remaining pnodes */
+ for (i = 1; i < cnt; i++) {
+ if (len + c->pnode_sz > c->leb_size) {
+ alen = ALIGN(len, c->min_io_size);
+ set_ltab(c, lnum, c->leb_size - alen, alen - len);
+ memset(p, 0xff, alen - len);
+ err = ubifs_leb_change(c, lnum++, buf, alen);
+ if (err)
+ goto out;
+ p = buf;
+ len = 0;
+ }
+ ubifs_pack_pnode(c, p, pnode);
+ p += c->pnode_sz;
+ len += c->pnode_sz;
+ /*
+ * pnodes are simply numbered left to right starting at zero,
+ * which means the pnode number can be used easily to traverse
+ * down the tree to the corresponding pnode.
+ */
+ pnode->num += 1;
+ }
+
+ row = 0;
+ for (i = UBIFS_LPT_FANOUT; cnt > i; i <<= UBIFS_LPT_FANOUT_SHIFT)
+ row += 1;
+ /* Add all nnodes, one level at a time */
+ while (1) {
+ /* Number of internal nodes (nnodes) at next level */
+ cnt = DIV_ROUND_UP(cnt, UBIFS_LPT_FANOUT);
+ for (i = 0; i < cnt; i++) {
+ if (len + c->nnode_sz > c->leb_size) {
+ alen = ALIGN(len, c->min_io_size);
+ set_ltab(c, lnum, c->leb_size - alen,
+ alen - len);
+ memset(p, 0xff, alen - len);
+ err = ubifs_leb_change(c, lnum++, buf, alen);
+ if (err)
+ goto out;
+ p = buf;
+ len = 0;
+ }
+ /* Only 1 nnode at this level, so it is the root */
+ if (cnt == 1) {
+ c->lpt_lnum = lnum;
+ c->lpt_offs = len;
+ }
+ /* Set branches to the level below */
+ for (j = 0; j < UBIFS_LPT_FANOUT; j++) {
+ if (bcnt) {
+ if (boffs + bsz > c->leb_size) {
+ blnum += 1;
+ boffs = 0;
+ }
+ nnode->nbranch[j].lnum = blnum;
+ nnode->nbranch[j].offs = boffs;
+ boffs += bsz;
+ bcnt--;
+ } else {
+ nnode->nbranch[j].lnum = 0;
+ nnode->nbranch[j].offs = 0;
+ }
+ }
+ nnode->num = calc_nnode_num(row, i);
+ ubifs_pack_nnode(c, p, nnode);
+ p += c->nnode_sz;
+ len += c->nnode_sz;
+ }
+ /* Only 1 nnode at this level, so it is the root */
+ if (cnt == 1)
+ break;
+ /* Update the information about the level below */
+ bcnt = cnt;
+ bsz = c->nnode_sz;
+ row -= 1;
+ }
+
+ if (*big_lpt) {
+ /* Need to add LPT's save table */
+ if (len + c->lsave_sz > c->leb_size) {
+ alen = ALIGN(len, c->min_io_size);
+ set_ltab(c, lnum, c->leb_size - alen, alen - len);
+ memset(p, 0xff, alen - len);
+ err = ubifs_leb_change(c, lnum++, buf, alen);
+ if (err)
+ goto out;
+ p = buf;
+ len = 0;
+ }
+
+ c->lsave_lnum = lnum;
+ c->lsave_offs = len;
+
+ for (i = 0; i < c->lsave_cnt && i < *main_lebs; i++)
+ lsave[i] = c->main_first + i;
+ for (; i < c->lsave_cnt; i++)
+ lsave[i] = c->main_first;
+
+ ubifs_pack_lsave(c, p, lsave);
+ p += c->lsave_sz;
+ len += c->lsave_sz;
+ }
+
+ /* Need to add LPT's own LEB properties table */
+ if (len + c->ltab_sz > c->leb_size) {
+ alen = ALIGN(len, c->min_io_size);
+ set_ltab(c, lnum, c->leb_size - alen, alen - len);
+ memset(p, 0xff, alen - len);
+ err = ubifs_leb_change(c, lnum++, buf, alen);
+ if (err)
+ goto out;
+ p = buf;
+ len = 0;
+ }
+
+ c->ltab_lnum = lnum;
+ c->ltab_offs = len;
+
+ /* Update ltab before packing it */
+ len += c->ltab_sz;
+ alen = ALIGN(len, c->min_io_size);
+ set_ltab(c, lnum, c->leb_size - alen, alen - len);
+
+ ubifs_pack_ltab(c, p, ltab);
+ p += c->ltab_sz;
+
+ /* Write remaining buffer */
+ memset(p, 0xff, alen - len);
+ err = ubifs_leb_change(c, lnum, buf, alen);
+ if (err)
+ goto out;
+
+ c->nhead_lnum = lnum;
+ c->nhead_offs = ALIGN(len, c->min_io_size);
+
+ dbg_lp("space_bits %d", c->space_bits);
+ dbg_lp("lpt_lnum_bits %d", c->lpt_lnum_bits);
+ dbg_lp("lpt_offs_bits %d", c->lpt_offs_bits);
+ dbg_lp("lpt_spc_bits %d", c->lpt_spc_bits);
+ dbg_lp("pcnt_bits %d", c->pcnt_bits);
+ dbg_lp("lnum_bits %d", c->lnum_bits);
+ dbg_lp("pnode_sz %d", c->pnode_sz);
+ dbg_lp("nnode_sz %d", c->nnode_sz);
+ dbg_lp("ltab_sz %d", c->ltab_sz);
+ dbg_lp("lsave_sz %d", c->lsave_sz);
+ dbg_lp("lsave_cnt %d", c->lsave_cnt);
+ dbg_lp("lpt_hght %d", c->lpt_hght);
+ dbg_lp("big_lpt %d", c->big_lpt);
+ dbg_lp("LPT root is at %d:%d", c->lpt_lnum, c->lpt_offs);
+ dbg_lp("LPT head is at %d:%d", c->nhead_lnum, c->nhead_offs);
+ dbg_lp("LPT ltab is at %d:%d", c->ltab_lnum, c->ltab_offs);
+ if (c->big_lpt)
+ dbg_lp("LPT lsave is at %d:%d", c->lsave_lnum, c->lsave_offs);
+out:
+ c->ltab = NULL;
+ kfree(lsave);
+ vfree(ltab);
+ vfree(buf);
+ kfree(nnode);
+ kfree(pnode);
+ return err;
+}
+
+/**
+ * update_cats - add LEB properties of a pnode to LEB category lists and heaps.
+ * @c: UBIFS file-system description object
+ * @pnode: pnode
+ *
+ * When a pnode is loaded into memory, the LEB properties it contains are added,
+ * by this function, to the LEB category lists and heaps.
+ */
+static void update_cats(struct ubifs_info *c, struct ubifs_pnode *pnode)
+{
+ int i;
+
+ for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
+ int cat = pnode->lprops[i].flags & LPROPS_CAT_MASK;
+ int lnum = pnode->lprops[i].lnum;
+
+ if (!lnum)
+ return;
+ ubifs_add_to_cat(c, &pnode->lprops[i], cat);
+ }
+}
+
+/**
+ * replace_cats - add LEB properties of a pnode to LEB category lists and heaps.
+ * @c: UBIFS file-system description object
+ * @old_pnode: pnode copied
+ * @new_pnode: pnode copy
+ *
+ * During commit it is sometimes necessary to copy a pnode
+ * (see dirty_cow_pnode). When that happens, references in
+ * category lists and heaps must be replaced. This function does that.
+ */
+static void replace_cats(struct ubifs_info *c, struct ubifs_pnode *old_pnode,
+ struct ubifs_pnode *new_pnode)
+{
+ int i;
+
+ for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
+ if (!new_pnode->lprops[i].lnum)
+ return;
+ ubifs_replace_cat(c, &old_pnode->lprops[i],
+ &new_pnode->lprops[i]);
+ }
+}
+
+/**
+ * check_lpt_crc - check LPT node crc is correct.
+ * @c: UBIFS file-system description object
+ * @buf: buffer containing node
+ * @len: length of node
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int check_lpt_crc(const struct ubifs_info *c, void *buf, int len)
+{
+ int pos = 0;
+ uint8_t *addr = buf;
+ uint16_t crc, calc_crc;
+
+ crc = ubifs_unpack_bits(&addr, &pos, UBIFS_LPT_CRC_BITS);
+ calc_crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES,
+ len - UBIFS_LPT_CRC_BYTES);
+ if (crc != calc_crc) {
+ ubifs_err(c, "invalid crc in LPT node: crc %hx calc %hx",
+ crc, calc_crc);
+ dump_stack();
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/**
+ * check_lpt_type - check LPT node type is correct.
+ * @c: UBIFS file-system description object
+ * @addr: address of type bit field is passed and returned updated here
+ * @pos: position of type bit field is passed and returned updated here
+ * @type: expected type
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int check_lpt_type(const struct ubifs_info *c, uint8_t **addr,
+ int *pos, int type)
+{
+ int node_type;
+
+ node_type = ubifs_unpack_bits(addr, pos, UBIFS_LPT_TYPE_BITS);
+ if (node_type != type) {
+ ubifs_err(c, "invalid type (%d) in LPT node type %d",
+ node_type, type);
+ dump_stack();
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/**
+ * unpack_pnode - unpack a pnode.
+ * @c: UBIFS file-system description object
+ * @buf: buffer containing packed pnode to unpack
+ * @pnode: pnode structure to fill
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int unpack_pnode(const struct ubifs_info *c, void *buf,
+ struct ubifs_pnode *pnode)
+{
+ uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES;
+ int i, pos = 0, err;
+
+ err = check_lpt_type(c, &addr, &pos, UBIFS_LPT_PNODE);
+ if (err)
+ return err;
+ if (c->big_lpt)
+ pnode->num = ubifs_unpack_bits(&addr, &pos, c->pcnt_bits);
+ for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
+ struct ubifs_lprops * const lprops = &pnode->lprops[i];
+
+ lprops->free = ubifs_unpack_bits(&addr, &pos, c->space_bits);
+ lprops->free <<= 3;
+ lprops->dirty = ubifs_unpack_bits(&addr, &pos, c->space_bits);
+ lprops->dirty <<= 3;
+
+ if (ubifs_unpack_bits(&addr, &pos, 1))
+ lprops->flags = LPROPS_INDEX;
+ else
+ lprops->flags = 0;
+ lprops->flags |= ubifs_categorize_lprops(c, lprops);
+ }
+ err = check_lpt_crc(c, buf, c->pnode_sz);
+ return err;
+}
+
+/**
+ * ubifs_unpack_nnode - unpack a nnode.
+ * @c: UBIFS file-system description object
+ * @buf: buffer containing packed nnode to unpack
+ * @nnode: nnode structure to fill
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int ubifs_unpack_nnode(const struct ubifs_info *c, void *buf,
+ struct ubifs_nnode *nnode)
+{
+ uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES;
+ int i, pos = 0, err;
+
+ err = check_lpt_type(c, &addr, &pos, UBIFS_LPT_NNODE);
+ if (err)
+ return err;
+ if (c->big_lpt)
+ nnode->num = ubifs_unpack_bits(&addr, &pos, c->pcnt_bits);
+ for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
+ int lnum;
+
+ lnum = ubifs_unpack_bits(&addr, &pos, c->lpt_lnum_bits) +
+ c->lpt_first;
+ if (lnum == c->lpt_last + 1)
+ lnum = 0;
+ nnode->nbranch[i].lnum = lnum;
+ nnode->nbranch[i].offs = ubifs_unpack_bits(&addr, &pos,
+ c->lpt_offs_bits);
+ }
+ err = check_lpt_crc(c, buf, c->nnode_sz);
+ return err;
+}
+
+/**
+ * unpack_ltab - unpack the LPT's own lprops table.
+ * @c: UBIFS file-system description object
+ * @buf: buffer from which to unpack
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int unpack_ltab(const struct ubifs_info *c, void *buf)
+{
+ uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES;
+ int i, pos = 0, err;
+
+ err = check_lpt_type(c, &addr, &pos, UBIFS_LPT_LTAB);
+ if (err)
+ return err;
+ for (i = 0; i < c->lpt_lebs; i++) {
+ int free = ubifs_unpack_bits(&addr, &pos, c->lpt_spc_bits);
+ int dirty = ubifs_unpack_bits(&addr, &pos, c->lpt_spc_bits);
+
+ if (free < 0 || free > c->leb_size || dirty < 0 ||
+ dirty > c->leb_size || free + dirty > c->leb_size)
+ return -EINVAL;
+
+ c->ltab[i].free = free;
+ c->ltab[i].dirty = dirty;
+ c->ltab[i].tgc = 0;
+ c->ltab[i].cmt = 0;
+ }
+ err = check_lpt_crc(c, buf, c->ltab_sz);
+ return err;
+}
+
+#ifndef __UBOOT__
+/**
+ * unpack_lsave - unpack the LPT's save table.
+ * @c: UBIFS file-system description object
+ * @buf: buffer from which to unpack
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int unpack_lsave(const struct ubifs_info *c, void *buf)
+{
+ uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES;
+ int i, pos = 0, err;
+
+ err = check_lpt_type(c, &addr, &pos, UBIFS_LPT_LSAVE);
+ if (err)
+ return err;
+ for (i = 0; i < c->lsave_cnt; i++) {
+ int lnum = ubifs_unpack_bits(&addr, &pos, c->lnum_bits);
+
+ if (lnum < c->main_first || lnum >= c->leb_cnt)
+ return -EINVAL;
+ c->lsave[i] = lnum;
+ }
+ err = check_lpt_crc(c, buf, c->lsave_sz);
+ return err;
+}
+#endif
+
+/**
+ * validate_nnode - validate a nnode.
+ * @c: UBIFS file-system description object
+ * @nnode: nnode to validate
+ * @parent: parent nnode (or NULL for the root nnode)
+ * @iip: index in parent
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int validate_nnode(const struct ubifs_info *c, struct ubifs_nnode *nnode,
+ struct ubifs_nnode *parent, int iip)
+{
+ int i, lvl, max_offs;
+
+ if (c->big_lpt) {
+ int num = calc_nnode_num_from_parent(c, parent, iip);
+
+ if (nnode->num != num)
+ return -EINVAL;
+ }
+ lvl = parent ? parent->level - 1 : c->lpt_hght;
+ if (lvl < 1)
+ return -EINVAL;
+ if (lvl == 1)
+ max_offs = c->leb_size - c->pnode_sz;
+ else
+ max_offs = c->leb_size - c->nnode_sz;
+ for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
+ int lnum = nnode->nbranch[i].lnum;
+ int offs = nnode->nbranch[i].offs;
+
+ if (lnum == 0) {
+ if (offs != 0)
+ return -EINVAL;
+ continue;
+ }
+ if (lnum < c->lpt_first || lnum > c->lpt_last)
+ return -EINVAL;
+ if (offs < 0 || offs > max_offs)
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/**
+ * validate_pnode - validate a pnode.
+ * @c: UBIFS file-system description object
+ * @pnode: pnode to validate
+ * @parent: parent nnode
+ * @iip: index in parent
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int validate_pnode(const struct ubifs_info *c, struct ubifs_pnode *pnode,
+ struct ubifs_nnode *parent, int iip)
+{
+ int i;
+
+ if (c->big_lpt) {
+ int num = calc_pnode_num_from_parent(c, parent, iip);
+
+ if (pnode->num != num)
+ return -EINVAL;
+ }
+ for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
+ int free = pnode->lprops[i].free;
+ int dirty = pnode->lprops[i].dirty;
+
+ if (free < 0 || free > c->leb_size || free % c->min_io_size ||
+ (free & 7))
+ return -EINVAL;
+ if (dirty < 0 || dirty > c->leb_size || (dirty & 7))
+ return -EINVAL;
+ if (dirty + free > c->leb_size)
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/**
+ * set_pnode_lnum - set LEB numbers on a pnode.
+ * @c: UBIFS file-system description object
+ * @pnode: pnode to update
+ *
+ * This function calculates the LEB numbers for the LEB properties it contains
+ * based on the pnode number.
+ */
+static void set_pnode_lnum(const struct ubifs_info *c,
+ struct ubifs_pnode *pnode)
+{
+ int i, lnum;
+
+ lnum = (pnode->num << UBIFS_LPT_FANOUT_SHIFT) + c->main_first;
+ for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
+ if (lnum >= c->leb_cnt)
+ return;
+ pnode->lprops[i].lnum = lnum++;
+ }
+}
+
+/**
+ * ubifs_read_nnode - read a nnode from flash and link it to the tree in memory.
+ * @c: UBIFS file-system description object
+ * @parent: parent nnode (or NULL for the root)
+ * @iip: index in parent
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int ubifs_read_nnode(struct ubifs_info *c, struct ubifs_nnode *parent, int iip)
+{
+ struct ubifs_nbranch *branch = NULL;
+ struct ubifs_nnode *nnode = NULL;
+ void *buf = c->lpt_nod_buf;
+ int err, lnum, offs;
+
+ if (parent) {
+ branch = &parent->nbranch[iip];
+ lnum = branch->lnum;
+ offs = branch->offs;
+ } else {
+ lnum = c->lpt_lnum;
+ offs = c->lpt_offs;
+ }
+ nnode = kzalloc(sizeof(struct ubifs_nnode), GFP_NOFS);
+ if (!nnode) {
+ err = -ENOMEM;
+ goto out;
+ }
+ if (lnum == 0) {
+ /*
+ * This nnode was not written which just means that the LEB
+ * properties in the subtree below it describe empty LEBs. We
+ * make the nnode as though we had read it, which in fact means
+ * doing almost nothing.
+ */
+ if (c->big_lpt)
+ nnode->num = calc_nnode_num_from_parent(c, parent, iip);
+ } else {
+ err = ubifs_leb_read(c, lnum, buf, offs, c->nnode_sz, 1);
+ if (err)
+ goto out;
+ err = ubifs_unpack_nnode(c, buf, nnode);
+ if (err)
+ goto out;
+ }
+ err = validate_nnode(c, nnode, parent, iip);
+ if (err)
+ goto out;
+ if (!c->big_lpt)
+ nnode->num = calc_nnode_num_from_parent(c, parent, iip);
+ if (parent) {
+ branch->nnode = nnode;
+ nnode->level = parent->level - 1;
+ } else {
+ c->nroot = nnode;
+ nnode->level = c->lpt_hght;
+ }
+ nnode->parent = parent;
+ nnode->iip = iip;
+ return 0;
+
+out:
+ ubifs_err(c, "error %d reading nnode at %d:%d", err, lnum, offs);
+ dump_stack();
+ kfree(nnode);
+ return err;
+}
+
+/**
+ * read_pnode - read a pnode from flash and link it to the tree in memory.
+ * @c: UBIFS file-system description object
+ * @parent: parent nnode
+ * @iip: index in parent
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int read_pnode(struct ubifs_info *c, struct ubifs_nnode *parent, int iip)
+{
+ struct ubifs_nbranch *branch;
+ struct ubifs_pnode *pnode = NULL;
+ void *buf = c->lpt_nod_buf;
+ int err, lnum, offs;
+
+ branch = &parent->nbranch[iip];
+ lnum = branch->lnum;
+ offs = branch->offs;
+ pnode = kzalloc(sizeof(struct ubifs_pnode), GFP_NOFS);
+ if (!pnode)
+ return -ENOMEM;
+
+ if (lnum == 0) {
+ /*
+ * This pnode was not written which just means that the LEB
+ * properties in it describe empty LEBs. We make the pnode as
+ * though we had read it.
+ */
+ int i;
+
+ if (c->big_lpt)
+ pnode->num = calc_pnode_num_from_parent(c, parent, iip);
+ for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
+ struct ubifs_lprops * const lprops = &pnode->lprops[i];
+
+ lprops->free = c->leb_size;
+ lprops->flags = ubifs_categorize_lprops(c, lprops);
+ }
+ } else {
+ err = ubifs_leb_read(c, lnum, buf, offs, c->pnode_sz, 1);
+ if (err)
+ goto out;
+ err = unpack_pnode(c, buf, pnode);
+ if (err)
+ goto out;
+ }
+ err = validate_pnode(c, pnode, parent, iip);
+ if (err)
+ goto out;
+ if (!c->big_lpt)
+ pnode->num = calc_pnode_num_from_parent(c, parent, iip);
+ branch->pnode = pnode;
+ pnode->parent = parent;
+ pnode->iip = iip;
+ set_pnode_lnum(c, pnode);
+ c->pnodes_have += 1;
+ return 0;
+
+out:
+ ubifs_err(c, "error %d reading pnode at %d:%d", err, lnum, offs);
+ ubifs_dump_pnode(c, pnode, parent, iip);
+ dump_stack();
+ ubifs_err(c, "calc num: %d", calc_pnode_num_from_parent(c, parent, iip));
+ kfree(pnode);
+ return err;
+}
+
+/**
+ * read_ltab - read LPT's own lprops table.
+ * @c: UBIFS file-system description object
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int read_ltab(struct ubifs_info *c)
+{
+ int err;
+ void *buf;
+
+ buf = vmalloc(c->ltab_sz);
+ if (!buf)
+ return -ENOMEM;
+ err = ubifs_leb_read(c, c->ltab_lnum, buf, c->ltab_offs, c->ltab_sz, 1);
+ if (err)
+ goto out;
+ err = unpack_ltab(c, buf);
+out:
+ vfree(buf);
+ return err;
+}
+
+#ifndef __UBOOT__
+/**
+ * read_lsave - read LPT's save table.
+ * @c: UBIFS file-system description object
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int read_lsave(struct ubifs_info *c)
+{
+ int err, i;
+ void *buf;
+
+ buf = vmalloc(c->lsave_sz);
+ if (!buf)
+ return -ENOMEM;
+ err = ubifs_leb_read(c, c->lsave_lnum, buf, c->lsave_offs,
+ c->lsave_sz, 1);
+ if (err)
+ goto out;
+ err = unpack_lsave(c, buf);
+ if (err)
+ goto out;
+ for (i = 0; i < c->lsave_cnt; i++) {
+ int lnum = c->lsave[i];
+ struct ubifs_lprops *lprops;
+
+ /*
+ * Due to automatic resizing, the values in the lsave table
+ * could be beyond the volume size - just ignore them.
+ */
+ if (lnum >= c->leb_cnt)
+ continue;
+ lprops = ubifs_lpt_lookup(c, lnum);
+ if (IS_ERR(lprops)) {
+ err = PTR_ERR(lprops);
+ goto out;
+ }
+ }
+out:
+ vfree(buf);
+ return err;
+}
+#endif
+
+/**
+ * ubifs_get_nnode - get a nnode.
+ * @c: UBIFS file-system description object
+ * @parent: parent nnode (or NULL for the root)
+ * @iip: index in parent
+ *
+ * This function returns a pointer to the nnode on success or a negative error
+ * code on failure.
+ */
+struct ubifs_nnode *ubifs_get_nnode(struct ubifs_info *c,
+ struct ubifs_nnode *parent, int iip)
+{
+ struct ubifs_nbranch *branch;
+ struct ubifs_nnode *nnode;
+ int err;
+
+ branch = &parent->nbranch[iip];
+ nnode = branch->nnode;
+ if (nnode)
+ return nnode;
+ err = ubifs_read_nnode(c, parent, iip);
+ if (err)
+ return ERR_PTR(err);
+ return branch->nnode;
+}
+
+/**
+ * ubifs_get_pnode - get a pnode.
+ * @c: UBIFS file-system description object
+ * @parent: parent nnode
+ * @iip: index in parent
+ *
+ * This function returns a pointer to the pnode on success or a negative error
+ * code on failure.
+ */
+struct ubifs_pnode *ubifs_get_pnode(struct ubifs_info *c,
+ struct ubifs_nnode *parent, int iip)
+{
+ struct ubifs_nbranch *branch;
+ struct ubifs_pnode *pnode;
+ int err;
+
+ branch = &parent->nbranch[iip];
+ pnode = branch->pnode;
+ if (pnode)
+ return pnode;
+ err = read_pnode(c, parent, iip);
+ if (err)
+ return ERR_PTR(err);
+ update_cats(c, branch->pnode);
+ return branch->pnode;
+}
+
+/**
+ * ubifs_lpt_lookup - lookup LEB properties in the LPT.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number to lookup
+ *
+ * This function returns a pointer to the LEB properties on success or a
+ * negative error code on failure.
+ */
+struct ubifs_lprops *ubifs_lpt_lookup(struct ubifs_info *c, int lnum)
+{
+ int err, i, h, iip, shft;
+ struct ubifs_nnode *nnode;
+ struct ubifs_pnode *pnode;
+
+ if (!c->nroot) {
+ err = ubifs_read_nnode(c, NULL, 0);
+ if (err)
+ return ERR_PTR(err);
+ }
+ nnode = c->nroot;
+ i = lnum - c->main_first;
+ shft = c->lpt_hght * UBIFS_LPT_FANOUT_SHIFT;
+ for (h = 1; h < c->lpt_hght; h++) {
+ iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1));
+ shft -= UBIFS_LPT_FANOUT_SHIFT;
+ nnode = ubifs_get_nnode(c, nnode, iip);
+ if (IS_ERR(nnode))
+ return ERR_CAST(nnode);
+ }
+ iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1));
+ pnode = ubifs_get_pnode(c, nnode, iip);
+ if (IS_ERR(pnode))
+ return ERR_CAST(pnode);
+ iip = (i & (UBIFS_LPT_FANOUT - 1));
+ dbg_lp("LEB %d, free %d, dirty %d, flags %d", lnum,
+ pnode->lprops[iip].free, pnode->lprops[iip].dirty,
+ pnode->lprops[iip].flags);
+ return &pnode->lprops[iip];
+}
+
+/**
+ * dirty_cow_nnode - ensure a nnode is not being committed.
+ * @c: UBIFS file-system description object
+ * @nnode: nnode to check
+ *
+ * Returns dirtied nnode on success or negative error code on failure.
+ */
+static struct ubifs_nnode *dirty_cow_nnode(struct ubifs_info *c,
+ struct ubifs_nnode *nnode)
+{
+ struct ubifs_nnode *n;
+ int i;
+
+ if (!test_bit(COW_CNODE, &nnode->flags)) {
+ /* nnode is not being committed */
+ if (!test_and_set_bit(DIRTY_CNODE, &nnode->flags)) {
+ c->dirty_nn_cnt += 1;
+ ubifs_add_nnode_dirt(c, nnode);
+ }
+ return nnode;
+ }
+
+ /* nnode is being committed, so copy it */
+ n = kmalloc(sizeof(struct ubifs_nnode), GFP_NOFS);
+ if (unlikely(!n))
+ return ERR_PTR(-ENOMEM);
+
+ memcpy(n, nnode, sizeof(struct ubifs_nnode));
+ n->cnext = NULL;
+ __set_bit(DIRTY_CNODE, &n->flags);
+ __clear_bit(COW_CNODE, &n->flags);
+
+ /* The children now have new parent */
+ for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
+ struct ubifs_nbranch *branch = &n->nbranch[i];
+
+ if (branch->cnode)
+ branch->cnode->parent = n;
+ }
+
+ ubifs_assert(!test_bit(OBSOLETE_CNODE, &nnode->flags));
+ __set_bit(OBSOLETE_CNODE, &nnode->flags);
+
+ c->dirty_nn_cnt += 1;
+ ubifs_add_nnode_dirt(c, nnode);
+ if (nnode->parent)
+ nnode->parent->nbranch[n->iip].nnode = n;
+ else
+ c->nroot = n;
+ return n;
+}
+
+/**
+ * dirty_cow_pnode - ensure a pnode is not being committed.
+ * @c: UBIFS file-system description object
+ * @pnode: pnode to check
+ *
+ * Returns dirtied pnode on success or negative error code on failure.
+ */
+static struct ubifs_pnode *dirty_cow_pnode(struct ubifs_info *c,
+ struct ubifs_pnode *pnode)
+{
+ struct ubifs_pnode *p;
+
+ if (!test_bit(COW_CNODE, &pnode->flags)) {
+ /* pnode is not being committed */
+ if (!test_and_set_bit(DIRTY_CNODE, &pnode->flags)) {
+ c->dirty_pn_cnt += 1;
+ add_pnode_dirt(c, pnode);
+ }
+ return pnode;
+ }
+
+ /* pnode is being committed, so copy it */
+ p = kmalloc(sizeof(struct ubifs_pnode), GFP_NOFS);
+ if (unlikely(!p))
+ return ERR_PTR(-ENOMEM);
+
+ memcpy(p, pnode, sizeof(struct ubifs_pnode));
+ p->cnext = NULL;
+ __set_bit(DIRTY_CNODE, &p->flags);
+ __clear_bit(COW_CNODE, &p->flags);
+ replace_cats(c, pnode, p);
+
+ ubifs_assert(!test_bit(OBSOLETE_CNODE, &pnode->flags));
+ __set_bit(OBSOLETE_CNODE, &pnode->flags);
+
+ c->dirty_pn_cnt += 1;
+ add_pnode_dirt(c, pnode);
+ pnode->parent->nbranch[p->iip].pnode = p;
+ return p;
+}
+
+/**
+ * ubifs_lpt_lookup_dirty - lookup LEB properties in the LPT.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number to lookup
+ *
+ * This function returns a pointer to the LEB properties on success or a
+ * negative error code on failure.
+ */
+struct ubifs_lprops *ubifs_lpt_lookup_dirty(struct ubifs_info *c, int lnum)
+{
+ int err, i, h, iip, shft;
+ struct ubifs_nnode *nnode;
+ struct ubifs_pnode *pnode;
+
+ if (!c->nroot) {
+ err = ubifs_read_nnode(c, NULL, 0);
+ if (err)
+ return ERR_PTR(err);
+ }
+ nnode = c->nroot;
+ nnode = dirty_cow_nnode(c, nnode);
+ if (IS_ERR(nnode))
+ return ERR_CAST(nnode);
+ i = lnum - c->main_first;
+ shft = c->lpt_hght * UBIFS_LPT_FANOUT_SHIFT;
+ for (h = 1; h < c->lpt_hght; h++) {
+ iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1));
+ shft -= UBIFS_LPT_FANOUT_SHIFT;
+ nnode = ubifs_get_nnode(c, nnode, iip);
+ if (IS_ERR(nnode))
+ return ERR_CAST(nnode);
+ nnode = dirty_cow_nnode(c, nnode);
+ if (IS_ERR(nnode))
+ return ERR_CAST(nnode);
+ }
+ iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1));
+ pnode = ubifs_get_pnode(c, nnode, iip);
+ if (IS_ERR(pnode))
+ return ERR_CAST(pnode);
+ pnode = dirty_cow_pnode(c, pnode);
+ if (IS_ERR(pnode))
+ return ERR_CAST(pnode);
+ iip = (i & (UBIFS_LPT_FANOUT - 1));
+ dbg_lp("LEB %d, free %d, dirty %d, flags %d", lnum,
+ pnode->lprops[iip].free, pnode->lprops[iip].dirty,
+ pnode->lprops[iip].flags);
+ ubifs_assert(test_bit(DIRTY_CNODE, &pnode->flags));
+ return &pnode->lprops[iip];
+}
+
+/**
+ * lpt_init_rd - initialize the LPT for reading.
+ * @c: UBIFS file-system description object
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int lpt_init_rd(struct ubifs_info *c)
+{
+ int err, i;
+
+ c->ltab = vmalloc(sizeof(struct ubifs_lpt_lprops) * c->lpt_lebs);
+ if (!c->ltab)
+ return -ENOMEM;
+
+ i = max_t(int, c->nnode_sz, c->pnode_sz);
+ c->lpt_nod_buf = kmalloc(i, GFP_KERNEL);
+ if (!c->lpt_nod_buf)
+ return -ENOMEM;
+
+ for (i = 0; i < LPROPS_HEAP_CNT; i++) {
+ c->lpt_heap[i].arr = kmalloc(sizeof(void *) * LPT_HEAP_SZ,
+ GFP_KERNEL);
+ if (!c->lpt_heap[i].arr)
+ return -ENOMEM;
+ c->lpt_heap[i].cnt = 0;
+ c->lpt_heap[i].max_cnt = LPT_HEAP_SZ;
+ }
+
+ c->dirty_idx.arr = kmalloc(sizeof(void *) * LPT_HEAP_SZ, GFP_KERNEL);
+ if (!c->dirty_idx.arr)
+ return -ENOMEM;
+ c->dirty_idx.cnt = 0;
+ c->dirty_idx.max_cnt = LPT_HEAP_SZ;
+
+ err = read_ltab(c);
+ if (err)
+ return err;
+
+ dbg_lp("space_bits %d", c->space_bits);
+ dbg_lp("lpt_lnum_bits %d", c->lpt_lnum_bits);
+ dbg_lp("lpt_offs_bits %d", c->lpt_offs_bits);
+ dbg_lp("lpt_spc_bits %d", c->lpt_spc_bits);
+ dbg_lp("pcnt_bits %d", c->pcnt_bits);
+ dbg_lp("lnum_bits %d", c->lnum_bits);
+ dbg_lp("pnode_sz %d", c->pnode_sz);
+ dbg_lp("nnode_sz %d", c->nnode_sz);
+ dbg_lp("ltab_sz %d", c->ltab_sz);
+ dbg_lp("lsave_sz %d", c->lsave_sz);
+ dbg_lp("lsave_cnt %d", c->lsave_cnt);
+ dbg_lp("lpt_hght %d", c->lpt_hght);
+ dbg_lp("big_lpt %d", c->big_lpt);
+ dbg_lp("LPT root is at %d:%d", c->lpt_lnum, c->lpt_offs);
+ dbg_lp("LPT head is at %d:%d", c->nhead_lnum, c->nhead_offs);
+ dbg_lp("LPT ltab is at %d:%d", c->ltab_lnum, c->ltab_offs);
+ if (c->big_lpt)
+ dbg_lp("LPT lsave is at %d:%d", c->lsave_lnum, c->lsave_offs);
+
+ return 0;
+}
+
+#ifndef __UBOOT__
+/**
+ * lpt_init_wr - initialize the LPT for writing.
+ * @c: UBIFS file-system description object
+ *
+ * 'lpt_init_rd()' must have been called already.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int lpt_init_wr(struct ubifs_info *c)
+{
+ int err, i;
+
+ c->ltab_cmt = vmalloc(sizeof(struct ubifs_lpt_lprops) * c->lpt_lebs);
+ if (!c->ltab_cmt)
+ return -ENOMEM;
+
+ c->lpt_buf = vmalloc(c->leb_size);
+ if (!c->lpt_buf)
+ return -ENOMEM;
+
+ if (c->big_lpt) {
+ c->lsave = kmalloc(sizeof(int) * c->lsave_cnt, GFP_NOFS);
+ if (!c->lsave)
+ return -ENOMEM;
+ err = read_lsave(c);
+ if (err)
+ return err;
+ }
+
+ for (i = 0; i < c->lpt_lebs; i++)
+ if (c->ltab[i].free == c->leb_size) {
+ err = ubifs_leb_unmap(c, i + c->lpt_first);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+#endif
+
+/**
+ * ubifs_lpt_init - initialize the LPT.
+ * @c: UBIFS file-system description object
+ * @rd: whether to initialize lpt for reading
+ * @wr: whether to initialize lpt for writing
+ *
+ * For mounting 'rw', @rd and @wr are both true. For mounting 'ro', @rd is true
+ * and @wr is false. For mounting from 'ro' to 'rw', @rd is false and @wr is
+ * true.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int ubifs_lpt_init(struct ubifs_info *c, int rd, int wr)
+{
+ int err;
+
+ if (rd) {
+ err = lpt_init_rd(c);
+ if (err)
+ goto out_err;
+ }
+
+#ifndef __UBOOT__
+ if (wr) {
+ err = lpt_init_wr(c);
+ if (err)
+ goto out_err;
+ }
+#endif
+
+ return 0;
+
+out_err:
+#ifndef __UBOOT__
+ if (wr)
+ ubifs_lpt_free(c, 1);
+#endif
+ if (rd)
+ ubifs_lpt_free(c, 0);
+ return err;
+}
+
+/**
+ * struct lpt_scan_node - somewhere to put nodes while we scan LPT.
+ * @nnode: where to keep a nnode
+ * @pnode: where to keep a pnode
+ * @cnode: where to keep a cnode
+ * @in_tree: is the node in the tree in memory
+ * @ptr.nnode: pointer to the nnode (if it is an nnode) which may be here or in
+ * the tree
+ * @ptr.pnode: ditto for pnode
+ * @ptr.cnode: ditto for cnode
+ */
+struct lpt_scan_node {
+ union {
+ struct ubifs_nnode nnode;
+ struct ubifs_pnode pnode;
+ struct ubifs_cnode cnode;
+ };
+ int in_tree;
+ union {
+ struct ubifs_nnode *nnode;
+ struct ubifs_pnode *pnode;
+ struct ubifs_cnode *cnode;
+ } ptr;
+};
+
+/**
+ * scan_get_nnode - for the scan, get a nnode from either the tree or flash.
+ * @c: the UBIFS file-system description object
+ * @path: where to put the nnode
+ * @parent: parent of the nnode
+ * @iip: index in parent of the nnode
+ *
+ * This function returns a pointer to the nnode on success or a negative error
+ * code on failure.
+ */
+static struct ubifs_nnode *scan_get_nnode(struct ubifs_info *c,
+ struct lpt_scan_node *path,
+ struct ubifs_nnode *parent, int iip)
+{
+ struct ubifs_nbranch *branch;
+ struct ubifs_nnode *nnode;
+ void *buf = c->lpt_nod_buf;
+ int err;
+
+ branch = &parent->nbranch[iip];
+ nnode = branch->nnode;
+ if (nnode) {
+ path->in_tree = 1;
+ path->ptr.nnode = nnode;
+ return nnode;
+ }
+ nnode = &path->nnode;
+ path->in_tree = 0;
+ path->ptr.nnode = nnode;
+ memset(nnode, 0, sizeof(struct ubifs_nnode));
+ if (branch->lnum == 0) {
+ /*
+ * This nnode was not written which just means that the LEB
+ * properties in the subtree below it describe empty LEBs. We
+ * make the nnode as though we had read it, which in fact means
+ * doing almost nothing.
+ */
+ if (c->big_lpt)
+ nnode->num = calc_nnode_num_from_parent(c, parent, iip);
+ } else {
+ err = ubifs_leb_read(c, branch->lnum, buf, branch->offs,
+ c->nnode_sz, 1);
+ if (err)
+ return ERR_PTR(err);
+ err = ubifs_unpack_nnode(c, buf, nnode);
+ if (err)
+ return ERR_PTR(err);
+ }
+ err = validate_nnode(c, nnode, parent, iip);
+ if (err)
+ return ERR_PTR(err);
+ if (!c->big_lpt)
+ nnode->num = calc_nnode_num_from_parent(c, parent, iip);
+ nnode->level = parent->level - 1;
+ nnode->parent = parent;
+ nnode->iip = iip;
+ return nnode;
+}
+
+/**
+ * scan_get_pnode - for the scan, get a pnode from either the tree or flash.
+ * @c: the UBIFS file-system description object
+ * @path: where to put the pnode
+ * @parent: parent of the pnode
+ * @iip: index in parent of the pnode
+ *
+ * This function returns a pointer to the pnode on success or a negative error
+ * code on failure.
+ */
+static struct ubifs_pnode *scan_get_pnode(struct ubifs_info *c,
+ struct lpt_scan_node *path,
+ struct ubifs_nnode *parent, int iip)
+{
+ struct ubifs_nbranch *branch;
+ struct ubifs_pnode *pnode;
+ void *buf = c->lpt_nod_buf;
+ int err;
+
+ branch = &parent->nbranch[iip];
+ pnode = branch->pnode;
+ if (pnode) {
+ path->in_tree = 1;
+ path->ptr.pnode = pnode;
+ return pnode;
+ }
+ pnode = &path->pnode;
+ path->in_tree = 0;
+ path->ptr.pnode = pnode;
+ memset(pnode, 0, sizeof(struct ubifs_pnode));
+ if (branch->lnum == 0) {
+ /*
+ * This pnode was not written which just means that the LEB
+ * properties in it describe empty LEBs. We make the pnode as
+ * though we had read it.
+ */
+ int i;
+
+ if (c->big_lpt)
+ pnode->num = calc_pnode_num_from_parent(c, parent, iip);
+ for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
+ struct ubifs_lprops * const lprops = &pnode->lprops[i];
+
+ lprops->free = c->leb_size;
+ lprops->flags = ubifs_categorize_lprops(c, lprops);
+ }
+ } else {
+ ubifs_assert(branch->lnum >= c->lpt_first &&
+ branch->lnum <= c->lpt_last);
+ ubifs_assert(branch->offs >= 0 && branch->offs < c->leb_size);
+ err = ubifs_leb_read(c, branch->lnum, buf, branch->offs,
+ c->pnode_sz, 1);
+ if (err)
+ return ERR_PTR(err);
+ err = unpack_pnode(c, buf, pnode);
+ if (err)
+ return ERR_PTR(err);
+ }
+ err = validate_pnode(c, pnode, parent, iip);
+ if (err)
+ return ERR_PTR(err);
+ if (!c->big_lpt)
+ pnode->num = calc_pnode_num_from_parent(c, parent, iip);
+ pnode->parent = parent;
+ pnode->iip = iip;
+ set_pnode_lnum(c, pnode);
+ return pnode;
+}
+
+/**
+ * ubifs_lpt_scan_nolock - scan the LPT.
+ * @c: the UBIFS file-system description object
+ * @start_lnum: LEB number from which to start scanning
+ * @end_lnum: LEB number at which to stop scanning
+ * @scan_cb: callback function called for each lprops
+ * @data: data to be passed to the callback function
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int ubifs_lpt_scan_nolock(struct ubifs_info *c, int start_lnum, int end_lnum,
+ ubifs_lpt_scan_callback scan_cb, void *data)
+{
+ int err = 0, i, h, iip, shft;
+ struct ubifs_nnode *nnode;
+ struct ubifs_pnode *pnode;
+ struct lpt_scan_node *path;
+
+ if (start_lnum == -1) {
+ start_lnum = end_lnum + 1;
+ if (start_lnum >= c->leb_cnt)
+ start_lnum = c->main_first;
+ }
+
+ ubifs_assert(start_lnum >= c->main_first && start_lnum < c->leb_cnt);
+ ubifs_assert(end_lnum >= c->main_first && end_lnum < c->leb_cnt);
+
+ if (!c->nroot) {
+ err = ubifs_read_nnode(c, NULL, 0);
+ if (err)
+ return err;
+ }
+
+ path = kmalloc(sizeof(struct lpt_scan_node) * (c->lpt_hght + 1),
+ GFP_NOFS);
+ if (!path)
+ return -ENOMEM;
+
+ path[0].ptr.nnode = c->nroot;
+ path[0].in_tree = 1;
+again:
+ /* Descend to the pnode containing start_lnum */
+ nnode = c->nroot;
+ i = start_lnum - c->main_first;
+ shft = c->lpt_hght * UBIFS_LPT_FANOUT_SHIFT;
+ for (h = 1; h < c->lpt_hght; h++) {
+ iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1));
+ shft -= UBIFS_LPT_FANOUT_SHIFT;
+ nnode = scan_get_nnode(c, path + h, nnode, iip);
+ if (IS_ERR(nnode)) {
+ err = PTR_ERR(nnode);
+ goto out;
+ }
+ }
+ iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1));
+ pnode = scan_get_pnode(c, path + h, nnode, iip);
+ if (IS_ERR(pnode)) {
+ err = PTR_ERR(pnode);
+ goto out;
+ }
+ iip = (i & (UBIFS_LPT_FANOUT - 1));
+
+ /* Loop for each lprops */
+ while (1) {
+ struct ubifs_lprops *lprops = &pnode->lprops[iip];
+ int ret, lnum = lprops->lnum;
+
+ ret = scan_cb(c, lprops, path[h].in_tree, data);
+ if (ret < 0) {
+ err = ret;
+ goto out;
+ }
+ if (ret & LPT_SCAN_ADD) {
+ /* Add all the nodes in path to the tree in memory */
+ for (h = 1; h < c->lpt_hght; h++) {
+ const size_t sz = sizeof(struct ubifs_nnode);
+ struct ubifs_nnode *parent;
+
+ if (path[h].in_tree)
+ continue;
+ nnode = kmemdup(&path[h].nnode, sz, GFP_NOFS);
+ if (!nnode) {
+ err = -ENOMEM;
+ goto out;
+ }
+ parent = nnode->parent;
+ parent->nbranch[nnode->iip].nnode = nnode;
+ path[h].ptr.nnode = nnode;
+ path[h].in_tree = 1;
+ path[h + 1].cnode.parent = nnode;
+ }
+ if (path[h].in_tree)
+ ubifs_ensure_cat(c, lprops);
+ else {
+ const size_t sz = sizeof(struct ubifs_pnode);
+ struct ubifs_nnode *parent;
+
+ pnode = kmemdup(&path[h].pnode, sz, GFP_NOFS);
+ if (!pnode) {
+ err = -ENOMEM;
+ goto out;
+ }
+ parent = pnode->parent;
+ parent->nbranch[pnode->iip].pnode = pnode;
+ path[h].ptr.pnode = pnode;
+ path[h].in_tree = 1;
+ update_cats(c, pnode);
+ c->pnodes_have += 1;
+ }
+ err = dbg_check_lpt_nodes(c, (struct ubifs_cnode *)
+ c->nroot, 0, 0);
+ if (err)
+ goto out;
+ err = dbg_check_cats(c);
+ if (err)
+ goto out;
+ }
+ if (ret & LPT_SCAN_STOP) {
+ err = 0;
+ break;
+ }
+ /* Get the next lprops */
+ if (lnum == end_lnum) {
+ /*
+ * We got to the end without finding what we were
+ * looking for
+ */
+ err = -ENOSPC;
+ goto out;
+ }
+ if (lnum + 1 >= c->leb_cnt) {
+ /* Wrap-around to the beginning */
+ start_lnum = c->main_first;
+ goto again;
+ }
+ if (iip + 1 < UBIFS_LPT_FANOUT) {
+ /* Next lprops is in the same pnode */
+ iip += 1;
+ continue;
+ }
+ /* We need to get the next pnode. Go up until we can go right */
+ iip = pnode->iip;
+ while (1) {
+ h -= 1;
+ ubifs_assert(h >= 0);
+ nnode = path[h].ptr.nnode;
+ if (iip + 1 < UBIFS_LPT_FANOUT)
+ break;
+ iip = nnode->iip;
+ }
+ /* Go right */
+ iip += 1;
+ /* Descend to the pnode */
+ h += 1;
+ for (; h < c->lpt_hght; h++) {
+ nnode = scan_get_nnode(c, path + h, nnode, iip);
+ if (IS_ERR(nnode)) {
+ err = PTR_ERR(nnode);
+ goto out;
+ }
+ iip = 0;
+ }
+ pnode = scan_get_pnode(c, path + h, nnode, iip);
+ if (IS_ERR(pnode)) {
+ err = PTR_ERR(pnode);
+ goto out;
+ }
+ iip = 0;
+ }
+out:
+ kfree(path);
+ return err;
+}
+
+/**
+ * dbg_chk_pnode - check a pnode.
+ * @c: the UBIFS file-system description object
+ * @pnode: pnode to check
+ * @col: pnode column
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int dbg_chk_pnode(struct ubifs_info *c, struct ubifs_pnode *pnode,
+ int col)
+{
+ int i;
+
+ if (pnode->num != col) {
+ ubifs_err(c, "pnode num %d expected %d parent num %d iip %d",
+ pnode->num, col, pnode->parent->num, pnode->iip);
+ return -EINVAL;
+ }
+ for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
+ struct ubifs_lprops *lp, *lprops = &pnode->lprops[i];
+ int lnum = (pnode->num << UBIFS_LPT_FANOUT_SHIFT) + i +
+ c->main_first;
+ int found, cat = lprops->flags & LPROPS_CAT_MASK;
+ struct ubifs_lpt_heap *heap;
+ struct list_head *list = NULL;
+
+ if (lnum >= c->leb_cnt)
+ continue;
+ if (lprops->lnum != lnum) {
+ ubifs_err(c, "bad LEB number %d expected %d",
+ lprops->lnum, lnum);
+ return -EINVAL;
+ }
+ if (lprops->flags & LPROPS_TAKEN) {
+ if (cat != LPROPS_UNCAT) {
+ ubifs_err(c, "LEB %d taken but not uncat %d",
+ lprops->lnum, cat);
+ return -EINVAL;
+ }
+ continue;
+ }
+ if (lprops->flags & LPROPS_INDEX) {
+ switch (cat) {
+ case LPROPS_UNCAT:
+ case LPROPS_DIRTY_IDX:
+ case LPROPS_FRDI_IDX:
+ break;
+ default:
+ ubifs_err(c, "LEB %d index but cat %d",
+ lprops->lnum, cat);
+ return -EINVAL;
+ }
+ } else {
+ switch (cat) {
+ case LPROPS_UNCAT:
+ case LPROPS_DIRTY:
+ case LPROPS_FREE:
+ case LPROPS_EMPTY:
+ case LPROPS_FREEABLE:
+ break;
+ default:
+ ubifs_err(c, "LEB %d not index but cat %d",
+ lprops->lnum, cat);
+ return -EINVAL;
+ }
+ }
+ switch (cat) {
+ case LPROPS_UNCAT:
+ list = &c->uncat_list;
+ break;
+ case LPROPS_EMPTY:
+ list = &c->empty_list;
+ break;
+ case LPROPS_FREEABLE:
+ list = &c->freeable_list;
+ break;
+ case LPROPS_FRDI_IDX:
+ list = &c->frdi_idx_list;
+ break;
+ }
+ found = 0;
+ switch (cat) {
+ case LPROPS_DIRTY:
+ case LPROPS_DIRTY_IDX:
+ case LPROPS_FREE:
+ heap = &c->lpt_heap[cat - 1];
+ if (lprops->hpos < heap->cnt &&
+ heap->arr[lprops->hpos] == lprops)
+ found = 1;
+ break;
+ case LPROPS_UNCAT:
+ case LPROPS_EMPTY:
+ case LPROPS_FREEABLE:
+ case LPROPS_FRDI_IDX:
+ list_for_each_entry(lp, list, list)
+ if (lprops == lp) {
+ found = 1;
+ break;
+ }
+ break;
+ }
+ if (!found) {
+ ubifs_err(c, "LEB %d cat %d not found in cat heap/list",
+ lprops->lnum, cat);
+ return -EINVAL;
+ }
+ switch (cat) {
+ case LPROPS_EMPTY:
+ if (lprops->free != c->leb_size) {
+ ubifs_err(c, "LEB %d cat %d free %d dirty %d",
+ lprops->lnum, cat, lprops->free,
+ lprops->dirty);
+ return -EINVAL;
+ }
+ break;
+ case LPROPS_FREEABLE:
+ case LPROPS_FRDI_IDX:
+ if (lprops->free + lprops->dirty != c->leb_size) {
+ ubifs_err(c, "LEB %d cat %d free %d dirty %d",
+ lprops->lnum, cat, lprops->free,
+ lprops->dirty);
+ return -EINVAL;
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+/**
+ * dbg_check_lpt_nodes - check nnodes and pnodes.
+ * @c: the UBIFS file-system description object
+ * @cnode: next cnode (nnode or pnode) to check
+ * @row: row of cnode (root is zero)
+ * @col: column of cnode (leftmost is zero)
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int dbg_check_lpt_nodes(struct ubifs_info *c, struct ubifs_cnode *cnode,
+ int row, int col)
+{
+ struct ubifs_nnode *nnode, *nn;
+ struct ubifs_cnode *cn;
+ int num, iip = 0, err;
+
+ if (!dbg_is_chk_lprops(c))
+ return 0;
+
+ while (cnode) {
+ ubifs_assert(row >= 0);
+ nnode = cnode->parent;
+ if (cnode->level) {
+ /* cnode is a nnode */
+ num = calc_nnode_num(row, col);
+ if (cnode->num != num) {
+ ubifs_err(c, "nnode num %d expected %d parent num %d iip %d",
+ cnode->num, num,
+ (nnode ? nnode->num : 0), cnode->iip);
+ return -EINVAL;
+ }
+ nn = (struct ubifs_nnode *)cnode;
+ while (iip < UBIFS_LPT_FANOUT) {
+ cn = nn->nbranch[iip].cnode;
+ if (cn) {
+ /* Go down */
+ row += 1;
+ col <<= UBIFS_LPT_FANOUT_SHIFT;
+ col += iip;
+ iip = 0;
+ cnode = cn;
+ break;
+ }
+ /* Go right */
+ iip += 1;
+ }
+ if (iip < UBIFS_LPT_FANOUT)
+ continue;
+ } else {
+ struct ubifs_pnode *pnode;
+
+ /* cnode is a pnode */
+ pnode = (struct ubifs_pnode *)cnode;
+ err = dbg_chk_pnode(c, pnode, col);
+ if (err)
+ return err;
+ }
+ /* Go up and to the right */
+ row -= 1;
+ col >>= UBIFS_LPT_FANOUT_SHIFT;
+ iip = cnode->iip + 1;
+ cnode = (struct ubifs_cnode *)nnode;
+ }
+ return 0;
+}
diff --git a/roms/u-boot/fs/ubifs/lpt_commit.c b/roms/u-boot/fs/ubifs/lpt_commit.c
new file mode 100644
index 000000000..897d00143
--- /dev/null
+++ b/roms/u-boot/fs/ubifs/lpt_commit.c
@@ -0,0 +1,2044 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ *
+ * Authors: Adrian Hunter
+ * Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * This file implements commit-related functionality of the LEB properties
+ * subsystem.
+ */
+
+#ifndef __UBOOT__
+#include <log.h>
+#include <dm/devres.h>
+#include <linux/crc16.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#else
+#include <linux/bitops.h>
+#include <linux/compat.h>
+#include <linux/err.h>
+#include "crc16.h"
+#endif
+#include "ubifs.h"
+
+#ifndef __UBOOT__
+static int dbg_populate_lsave(struct ubifs_info *c);
+#endif
+
+/**
+ * first_dirty_cnode - find first dirty cnode.
+ * @c: UBIFS file-system description object
+ * @nnode: nnode at which to start
+ *
+ * This function returns the first dirty cnode or %NULL if there is not one.
+ */
+static struct ubifs_cnode *first_dirty_cnode(struct ubifs_nnode *nnode)
+{
+ ubifs_assert(nnode);
+ while (1) {
+ int i, cont = 0;
+
+ for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
+ struct ubifs_cnode *cnode;
+
+ cnode = nnode->nbranch[i].cnode;
+ if (cnode &&
+ test_bit(DIRTY_CNODE, &cnode->flags)) {
+ if (cnode->level == 0)
+ return cnode;
+ nnode = (struct ubifs_nnode *)cnode;
+ cont = 1;
+ break;
+ }
+ }
+ if (!cont)
+ return (struct ubifs_cnode *)nnode;
+ }
+}
+
+/**
+ * next_dirty_cnode - find next dirty cnode.
+ * @cnode: cnode from which to begin searching
+ *
+ * This function returns the next dirty cnode or %NULL if there is not one.
+ */
+static struct ubifs_cnode *next_dirty_cnode(struct ubifs_cnode *cnode)
+{
+ struct ubifs_nnode *nnode;
+ int i;
+
+ ubifs_assert(cnode);
+ nnode = cnode->parent;
+ if (!nnode)
+ return NULL;
+ for (i = cnode->iip + 1; i < UBIFS_LPT_FANOUT; i++) {
+ cnode = nnode->nbranch[i].cnode;
+ if (cnode && test_bit(DIRTY_CNODE, &cnode->flags)) {
+ if (cnode->level == 0)
+ return cnode; /* cnode is a pnode */
+ /* cnode is a nnode */
+ return first_dirty_cnode((struct ubifs_nnode *)cnode);
+ }
+ }
+ return (struct ubifs_cnode *)nnode;
+}
+
+/**
+ * get_cnodes_to_commit - create list of dirty cnodes to commit.
+ * @c: UBIFS file-system description object
+ *
+ * This function returns the number of cnodes to commit.
+ */
+static int get_cnodes_to_commit(struct ubifs_info *c)
+{
+ struct ubifs_cnode *cnode, *cnext;
+ int cnt = 0;
+
+ if (!c->nroot)
+ return 0;
+
+ if (!test_bit(DIRTY_CNODE, &c->nroot->flags))
+ return 0;
+
+ c->lpt_cnext = first_dirty_cnode(c->nroot);
+ cnode = c->lpt_cnext;
+ if (!cnode)
+ return 0;
+ cnt += 1;
+ while (1) {
+ ubifs_assert(!test_bit(COW_CNODE, &cnode->flags));
+ __set_bit(COW_CNODE, &cnode->flags);
+ cnext = next_dirty_cnode(cnode);
+ if (!cnext) {
+ cnode->cnext = c->lpt_cnext;
+ break;
+ }
+ cnode->cnext = cnext;
+ cnode = cnext;
+ cnt += 1;
+ }
+ dbg_cmt("committing %d cnodes", cnt);
+ dbg_lp("committing %d cnodes", cnt);
+ ubifs_assert(cnt == c->dirty_nn_cnt + c->dirty_pn_cnt);
+ return cnt;
+}
+
+/**
+ * upd_ltab - update LPT LEB properties.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number
+ * @free: amount of free space
+ * @dirty: amount of dirty space to add
+ */
+static void upd_ltab(struct ubifs_info *c, int lnum, int free, int dirty)
+{
+ dbg_lp("LEB %d free %d dirty %d to %d +%d",
+ lnum, c->ltab[lnum - c->lpt_first].free,
+ c->ltab[lnum - c->lpt_first].dirty, free, dirty);
+ ubifs_assert(lnum >= c->lpt_first && lnum <= c->lpt_last);
+ c->ltab[lnum - c->lpt_first].free = free;
+ c->ltab[lnum - c->lpt_first].dirty += dirty;
+}
+
+/**
+ * alloc_lpt_leb - allocate an LPT LEB that is empty.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number is passed and returned here
+ *
+ * This function finds the next empty LEB in the ltab starting from @lnum. If a
+ * an empty LEB is found it is returned in @lnum and the function returns %0.
+ * Otherwise the function returns -ENOSPC. Note however, that LPT is designed
+ * never to run out of space.
+ */
+static int alloc_lpt_leb(struct ubifs_info *c, int *lnum)
+{
+ int i, n;
+
+ n = *lnum - c->lpt_first + 1;
+ for (i = n; i < c->lpt_lebs; i++) {
+ if (c->ltab[i].tgc || c->ltab[i].cmt)
+ continue;
+ if (c->ltab[i].free == c->leb_size) {
+ c->ltab[i].cmt = 1;
+ *lnum = i + c->lpt_first;
+ return 0;
+ }
+ }
+
+ for (i = 0; i < n; i++) {
+ if (c->ltab[i].tgc || c->ltab[i].cmt)
+ continue;
+ if (c->ltab[i].free == c->leb_size) {
+ c->ltab[i].cmt = 1;
+ *lnum = i + c->lpt_first;
+ return 0;
+ }
+ }
+ return -ENOSPC;
+}
+
+/**
+ * layout_cnodes - layout cnodes for commit.
+ * @c: UBIFS file-system description object
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int layout_cnodes(struct ubifs_info *c)
+{
+ int lnum, offs, len, alen, done_lsave, done_ltab, err;
+ struct ubifs_cnode *cnode;
+
+ err = dbg_chk_lpt_sz(c, 0, 0);
+ if (err)
+ return err;
+ cnode = c->lpt_cnext;
+ if (!cnode)
+ return 0;
+ lnum = c->nhead_lnum;
+ offs = c->nhead_offs;
+ /* Try to place lsave and ltab nicely */
+ done_lsave = !c->big_lpt;
+ done_ltab = 0;
+ if (!done_lsave && offs + c->lsave_sz <= c->leb_size) {
+ done_lsave = 1;
+ c->lsave_lnum = lnum;
+ c->lsave_offs = offs;
+ offs += c->lsave_sz;
+ dbg_chk_lpt_sz(c, 1, c->lsave_sz);
+ }
+
+ if (offs + c->ltab_sz <= c->leb_size) {
+ done_ltab = 1;
+ c->ltab_lnum = lnum;
+ c->ltab_offs = offs;
+ offs += c->ltab_sz;
+ dbg_chk_lpt_sz(c, 1, c->ltab_sz);
+ }
+
+ do {
+ if (cnode->level) {
+ len = c->nnode_sz;
+ c->dirty_nn_cnt -= 1;
+ } else {
+ len = c->pnode_sz;
+ c->dirty_pn_cnt -= 1;
+ }
+ while (offs + len > c->leb_size) {
+ alen = ALIGN(offs, c->min_io_size);
+ upd_ltab(c, lnum, c->leb_size - alen, alen - offs);
+ dbg_chk_lpt_sz(c, 2, c->leb_size - offs);
+ err = alloc_lpt_leb(c, &lnum);
+ if (err)
+ goto no_space;
+ offs = 0;
+ ubifs_assert(lnum >= c->lpt_first &&
+ lnum <= c->lpt_last);
+ /* Try to place lsave and ltab nicely */
+ if (!done_lsave) {
+ done_lsave = 1;
+ c->lsave_lnum = lnum;
+ c->lsave_offs = offs;
+ offs += c->lsave_sz;
+ dbg_chk_lpt_sz(c, 1, c->lsave_sz);
+ continue;
+ }
+ if (!done_ltab) {
+ done_ltab = 1;
+ c->ltab_lnum = lnum;
+ c->ltab_offs = offs;
+ offs += c->ltab_sz;
+ dbg_chk_lpt_sz(c, 1, c->ltab_sz);
+ continue;
+ }
+ break;
+ }
+ if (cnode->parent) {
+ cnode->parent->nbranch[cnode->iip].lnum = lnum;
+ cnode->parent->nbranch[cnode->iip].offs = offs;
+ } else {
+ c->lpt_lnum = lnum;
+ c->lpt_offs = offs;
+ }
+ offs += len;
+ dbg_chk_lpt_sz(c, 1, len);
+ cnode = cnode->cnext;
+ } while (cnode && cnode != c->lpt_cnext);
+
+ /* Make sure to place LPT's save table */
+ if (!done_lsave) {
+ if (offs + c->lsave_sz > c->leb_size) {
+ alen = ALIGN(offs, c->min_io_size);
+ upd_ltab(c, lnum, c->leb_size - alen, alen - offs);
+ dbg_chk_lpt_sz(c, 2, c->leb_size - offs);
+ err = alloc_lpt_leb(c, &lnum);
+ if (err)
+ goto no_space;
+ offs = 0;
+ ubifs_assert(lnum >= c->lpt_first &&
+ lnum <= c->lpt_last);
+ }
+ done_lsave = 1;
+ c->lsave_lnum = lnum;
+ c->lsave_offs = offs;
+ offs += c->lsave_sz;
+ dbg_chk_lpt_sz(c, 1, c->lsave_sz);
+ }
+
+ /* Make sure to place LPT's own lprops table */
+ if (!done_ltab) {
+ if (offs + c->ltab_sz > c->leb_size) {
+ alen = ALIGN(offs, c->min_io_size);
+ upd_ltab(c, lnum, c->leb_size - alen, alen - offs);
+ dbg_chk_lpt_sz(c, 2, c->leb_size - offs);
+ err = alloc_lpt_leb(c, &lnum);
+ if (err)
+ goto no_space;
+ offs = 0;
+ ubifs_assert(lnum >= c->lpt_first &&
+ lnum <= c->lpt_last);
+ }
+ c->ltab_lnum = lnum;
+ c->ltab_offs = offs;
+ offs += c->ltab_sz;
+ dbg_chk_lpt_sz(c, 1, c->ltab_sz);
+ }
+
+ alen = ALIGN(offs, c->min_io_size);
+ upd_ltab(c, lnum, c->leb_size - alen, alen - offs);
+ dbg_chk_lpt_sz(c, 4, alen - offs);
+ err = dbg_chk_lpt_sz(c, 3, alen);
+ if (err)
+ return err;
+ return 0;
+
+no_space:
+ ubifs_err(c, "LPT out of space at LEB %d:%d needing %d, done_ltab %d, done_lsave %d",
+ lnum, offs, len, done_ltab, done_lsave);
+ ubifs_dump_lpt_info(c);
+ ubifs_dump_lpt_lebs(c);
+ dump_stack();
+ return err;
+}
+
+#ifndef __UBOOT__
+/**
+ * realloc_lpt_leb - allocate an LPT LEB that is empty.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number is passed and returned here
+ *
+ * This function duplicates exactly the results of the function alloc_lpt_leb.
+ * It is used during end commit to reallocate the same LEB numbers that were
+ * allocated by alloc_lpt_leb during start commit.
+ *
+ * This function finds the next LEB that was allocated by the alloc_lpt_leb
+ * function starting from @lnum. If a LEB is found it is returned in @lnum and
+ * the function returns %0. Otherwise the function returns -ENOSPC.
+ * Note however, that LPT is designed never to run out of space.
+ */
+static int realloc_lpt_leb(struct ubifs_info *c, int *lnum)
+{
+ int i, n;
+
+ n = *lnum - c->lpt_first + 1;
+ for (i = n; i < c->lpt_lebs; i++)
+ if (c->ltab[i].cmt) {
+ c->ltab[i].cmt = 0;
+ *lnum = i + c->lpt_first;
+ return 0;
+ }
+
+ for (i = 0; i < n; i++)
+ if (c->ltab[i].cmt) {
+ c->ltab[i].cmt = 0;
+ *lnum = i + c->lpt_first;
+ return 0;
+ }
+ return -ENOSPC;
+}
+
+/**
+ * write_cnodes - write cnodes for commit.
+ * @c: UBIFS file-system description object
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int write_cnodes(struct ubifs_info *c)
+{
+ int lnum, offs, len, from, err, wlen, alen, done_ltab, done_lsave;
+ struct ubifs_cnode *cnode;
+ void *buf = c->lpt_buf;
+
+ cnode = c->lpt_cnext;
+ if (!cnode)
+ return 0;
+ lnum = c->nhead_lnum;
+ offs = c->nhead_offs;
+ from = offs;
+ /* Ensure empty LEB is unmapped */
+ if (offs == 0) {
+ err = ubifs_leb_unmap(c, lnum);
+ if (err)
+ return err;
+ }
+ /* Try to place lsave and ltab nicely */
+ done_lsave = !c->big_lpt;
+ done_ltab = 0;
+ if (!done_lsave && offs + c->lsave_sz <= c->leb_size) {
+ done_lsave = 1;
+ ubifs_pack_lsave(c, buf + offs, c->lsave);
+ offs += c->lsave_sz;
+ dbg_chk_lpt_sz(c, 1, c->lsave_sz);
+ }
+
+ if (offs + c->ltab_sz <= c->leb_size) {
+ done_ltab = 1;
+ ubifs_pack_ltab(c, buf + offs, c->ltab_cmt);
+ offs += c->ltab_sz;
+ dbg_chk_lpt_sz(c, 1, c->ltab_sz);
+ }
+
+ /* Loop for each cnode */
+ do {
+ if (cnode->level)
+ len = c->nnode_sz;
+ else
+ len = c->pnode_sz;
+ while (offs + len > c->leb_size) {
+ wlen = offs - from;
+ if (wlen) {
+ alen = ALIGN(wlen, c->min_io_size);
+ memset(buf + offs, 0xff, alen - wlen);
+ err = ubifs_leb_write(c, lnum, buf + from, from,
+ alen);
+ if (err)
+ return err;
+ }
+ dbg_chk_lpt_sz(c, 2, c->leb_size - offs);
+ err = realloc_lpt_leb(c, &lnum);
+ if (err)
+ goto no_space;
+ offs = from = 0;
+ ubifs_assert(lnum >= c->lpt_first &&
+ lnum <= c->lpt_last);
+ err = ubifs_leb_unmap(c, lnum);
+ if (err)
+ return err;
+ /* Try to place lsave and ltab nicely */
+ if (!done_lsave) {
+ done_lsave = 1;
+ ubifs_pack_lsave(c, buf + offs, c->lsave);
+ offs += c->lsave_sz;
+ dbg_chk_lpt_sz(c, 1, c->lsave_sz);
+ continue;
+ }
+ if (!done_ltab) {
+ done_ltab = 1;
+ ubifs_pack_ltab(c, buf + offs, c->ltab_cmt);
+ offs += c->ltab_sz;
+ dbg_chk_lpt_sz(c, 1, c->ltab_sz);
+ continue;
+ }
+ break;
+ }
+ if (cnode->level)
+ ubifs_pack_nnode(c, buf + offs,
+ (struct ubifs_nnode *)cnode);
+ else
+ ubifs_pack_pnode(c, buf + offs,
+ (struct ubifs_pnode *)cnode);
+ /*
+ * The reason for the barriers is the same as in case of TNC.
+ * See comment in 'write_index()'. 'dirty_cow_nnode()' and
+ * 'dirty_cow_pnode()' are the functions for which this is
+ * important.
+ */
+ clear_bit(DIRTY_CNODE, &cnode->flags);
+ smp_mb__before_atomic();
+ clear_bit(COW_CNODE, &cnode->flags);
+ smp_mb__after_atomic();
+ offs += len;
+ dbg_chk_lpt_sz(c, 1, len);
+ cnode = cnode->cnext;
+ } while (cnode && cnode != c->lpt_cnext);
+
+ /* Make sure to place LPT's save table */
+ if (!done_lsave) {
+ if (offs + c->lsave_sz > c->leb_size) {
+ wlen = offs - from;
+ alen = ALIGN(wlen, c->min_io_size);
+ memset(buf + offs, 0xff, alen - wlen);
+ err = ubifs_leb_write(c, lnum, buf + from, from, alen);
+ if (err)
+ return err;
+ dbg_chk_lpt_sz(c, 2, c->leb_size - offs);
+ err = realloc_lpt_leb(c, &lnum);
+ if (err)
+ goto no_space;
+ offs = from = 0;
+ ubifs_assert(lnum >= c->lpt_first &&
+ lnum <= c->lpt_last);
+ err = ubifs_leb_unmap(c, lnum);
+ if (err)
+ return err;
+ }
+ done_lsave = 1;
+ ubifs_pack_lsave(c, buf + offs, c->lsave);
+ offs += c->lsave_sz;
+ dbg_chk_lpt_sz(c, 1, c->lsave_sz);
+ }
+
+ /* Make sure to place LPT's own lprops table */
+ if (!done_ltab) {
+ if (offs + c->ltab_sz > c->leb_size) {
+ wlen = offs - from;
+ alen = ALIGN(wlen, c->min_io_size);
+ memset(buf + offs, 0xff, alen - wlen);
+ err = ubifs_leb_write(c, lnum, buf + from, from, alen);
+ if (err)
+ return err;
+ dbg_chk_lpt_sz(c, 2, c->leb_size - offs);
+ err = realloc_lpt_leb(c, &lnum);
+ if (err)
+ goto no_space;
+ offs = from = 0;
+ ubifs_assert(lnum >= c->lpt_first &&
+ lnum <= c->lpt_last);
+ err = ubifs_leb_unmap(c, lnum);
+ if (err)
+ return err;
+ }
+ ubifs_pack_ltab(c, buf + offs, c->ltab_cmt);
+ offs += c->ltab_sz;
+ dbg_chk_lpt_sz(c, 1, c->ltab_sz);
+ }
+
+ /* Write remaining data in buffer */
+ wlen = offs - from;
+ alen = ALIGN(wlen, c->min_io_size);
+ memset(buf + offs, 0xff, alen - wlen);
+ err = ubifs_leb_write(c, lnum, buf + from, from, alen);
+ if (err)
+ return err;
+
+ dbg_chk_lpt_sz(c, 4, alen - wlen);
+ err = dbg_chk_lpt_sz(c, 3, ALIGN(offs, c->min_io_size));
+ if (err)
+ return err;
+
+ c->nhead_lnum = lnum;
+ c->nhead_offs = ALIGN(offs, c->min_io_size);
+
+ dbg_lp("LPT root is at %d:%d", c->lpt_lnum, c->lpt_offs);
+ dbg_lp("LPT head is at %d:%d", c->nhead_lnum, c->nhead_offs);
+ dbg_lp("LPT ltab is at %d:%d", c->ltab_lnum, c->ltab_offs);
+ if (c->big_lpt)
+ dbg_lp("LPT lsave is at %d:%d", c->lsave_lnum, c->lsave_offs);
+
+ return 0;
+
+no_space:
+ ubifs_err(c, "LPT out of space mismatch at LEB %d:%d needing %d, done_ltab %d, done_lsave %d",
+ lnum, offs, len, done_ltab, done_lsave);
+ ubifs_dump_lpt_info(c);
+ ubifs_dump_lpt_lebs(c);
+ dump_stack();
+ return err;
+}
+#endif
+
+/**
+ * next_pnode_to_dirty - find next pnode to dirty.
+ * @c: UBIFS file-system description object
+ * @pnode: pnode
+ *
+ * This function returns the next pnode to dirty or %NULL if there are no more
+ * pnodes. Note that pnodes that have never been written (lnum == 0) are
+ * skipped.
+ */
+static struct ubifs_pnode *next_pnode_to_dirty(struct ubifs_info *c,
+ struct ubifs_pnode *pnode)
+{
+ struct ubifs_nnode *nnode;
+ int iip;
+
+ /* Try to go right */
+ nnode = pnode->parent;
+ for (iip = pnode->iip + 1; iip < UBIFS_LPT_FANOUT; iip++) {
+ if (nnode->nbranch[iip].lnum)
+ return ubifs_get_pnode(c, nnode, iip);
+ }
+
+ /* Go up while can't go right */
+ do {
+ iip = nnode->iip + 1;
+ nnode = nnode->parent;
+ if (!nnode)
+ return NULL;
+ for (; iip < UBIFS_LPT_FANOUT; iip++) {
+ if (nnode->nbranch[iip].lnum)
+ break;
+ }
+ } while (iip >= UBIFS_LPT_FANOUT);
+
+ /* Go right */
+ nnode = ubifs_get_nnode(c, nnode, iip);
+ if (IS_ERR(nnode))
+ return (void *)nnode;
+
+ /* Go down to level 1 */
+ while (nnode->level > 1) {
+ for (iip = 0; iip < UBIFS_LPT_FANOUT; iip++) {
+ if (nnode->nbranch[iip].lnum)
+ break;
+ }
+ if (iip >= UBIFS_LPT_FANOUT) {
+ /*
+ * Should not happen, but we need to keep going
+ * if it does.
+ */
+ iip = 0;
+ }
+ nnode = ubifs_get_nnode(c, nnode, iip);
+ if (IS_ERR(nnode))
+ return (void *)nnode;
+ }
+
+ for (iip = 0; iip < UBIFS_LPT_FANOUT; iip++)
+ if (nnode->nbranch[iip].lnum)
+ break;
+ if (iip >= UBIFS_LPT_FANOUT)
+ /* Should not happen, but we need to keep going if it does */
+ iip = 0;
+ return ubifs_get_pnode(c, nnode, iip);
+}
+
+/**
+ * pnode_lookup - lookup a pnode in the LPT.
+ * @c: UBIFS file-system description object
+ * @i: pnode number (0 to main_lebs - 1)
+ *
+ * This function returns a pointer to the pnode on success or a negative
+ * error code on failure.
+ */
+static struct ubifs_pnode *pnode_lookup(struct ubifs_info *c, int i)
+{
+ int err, h, iip, shft;
+ struct ubifs_nnode *nnode;
+
+ if (!c->nroot) {
+ err = ubifs_read_nnode(c, NULL, 0);
+ if (err)
+ return ERR_PTR(err);
+ }
+ i <<= UBIFS_LPT_FANOUT_SHIFT;
+ nnode = c->nroot;
+ shft = c->lpt_hght * UBIFS_LPT_FANOUT_SHIFT;
+ for (h = 1; h < c->lpt_hght; h++) {
+ iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1));
+ shft -= UBIFS_LPT_FANOUT_SHIFT;
+ nnode = ubifs_get_nnode(c, nnode, iip);
+ if (IS_ERR(nnode))
+ return ERR_CAST(nnode);
+ }
+ iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1));
+ return ubifs_get_pnode(c, nnode, iip);
+}
+
+/**
+ * add_pnode_dirt - add dirty space to LPT LEB properties.
+ * @c: UBIFS file-system description object
+ * @pnode: pnode for which to add dirt
+ */
+static void add_pnode_dirt(struct ubifs_info *c, struct ubifs_pnode *pnode)
+{
+ ubifs_add_lpt_dirt(c, pnode->parent->nbranch[pnode->iip].lnum,
+ c->pnode_sz);
+}
+
+/**
+ * do_make_pnode_dirty - mark a pnode dirty.
+ * @c: UBIFS file-system description object
+ * @pnode: pnode to mark dirty
+ */
+static void do_make_pnode_dirty(struct ubifs_info *c, struct ubifs_pnode *pnode)
+{
+ /* Assumes cnext list is empty i.e. not called during commit */
+ if (!test_and_set_bit(DIRTY_CNODE, &pnode->flags)) {
+ struct ubifs_nnode *nnode;
+
+ c->dirty_pn_cnt += 1;
+ add_pnode_dirt(c, pnode);
+ /* Mark parent and ancestors dirty too */
+ nnode = pnode->parent;
+ while (nnode) {
+ if (!test_and_set_bit(DIRTY_CNODE, &nnode->flags)) {
+ c->dirty_nn_cnt += 1;
+ ubifs_add_nnode_dirt(c, nnode);
+ nnode = nnode->parent;
+ } else
+ break;
+ }
+ }
+}
+
+/**
+ * make_tree_dirty - mark the entire LEB properties tree dirty.
+ * @c: UBIFS file-system description object
+ *
+ * This function is used by the "small" LPT model to cause the entire LEB
+ * properties tree to be written. The "small" LPT model does not use LPT
+ * garbage collection because it is more efficient to write the entire tree
+ * (because it is small).
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int make_tree_dirty(struct ubifs_info *c)
+{
+ struct ubifs_pnode *pnode;
+
+ pnode = pnode_lookup(c, 0);
+ if (IS_ERR(pnode))
+ return PTR_ERR(pnode);
+
+ while (pnode) {
+ do_make_pnode_dirty(c, pnode);
+ pnode = next_pnode_to_dirty(c, pnode);
+ if (IS_ERR(pnode))
+ return PTR_ERR(pnode);
+ }
+ return 0;
+}
+
+/**
+ * need_write_all - determine if the LPT area is running out of free space.
+ * @c: UBIFS file-system description object
+ *
+ * This function returns %1 if the LPT area is running out of free space and %0
+ * if it is not.
+ */
+static int need_write_all(struct ubifs_info *c)
+{
+ long long free = 0;
+ int i;
+
+ for (i = 0; i < c->lpt_lebs; i++) {
+ if (i + c->lpt_first == c->nhead_lnum)
+ free += c->leb_size - c->nhead_offs;
+ else if (c->ltab[i].free == c->leb_size)
+ free += c->leb_size;
+ else if (c->ltab[i].free + c->ltab[i].dirty == c->leb_size)
+ free += c->leb_size;
+ }
+ /* Less than twice the size left */
+ if (free <= c->lpt_sz * 2)
+ return 1;
+ return 0;
+}
+
+/**
+ * lpt_tgc_start - start trivial garbage collection of LPT LEBs.
+ * @c: UBIFS file-system description object
+ *
+ * LPT trivial garbage collection is where a LPT LEB contains only dirty and
+ * free space and so may be reused as soon as the next commit is completed.
+ * This function is called during start commit to mark LPT LEBs for trivial GC.
+ */
+static void lpt_tgc_start(struct ubifs_info *c)
+{
+ int i;
+
+ for (i = 0; i < c->lpt_lebs; i++) {
+ if (i + c->lpt_first == c->nhead_lnum)
+ continue;
+ if (c->ltab[i].dirty > 0 &&
+ c->ltab[i].free + c->ltab[i].dirty == c->leb_size) {
+ c->ltab[i].tgc = 1;
+ c->ltab[i].free = c->leb_size;
+ c->ltab[i].dirty = 0;
+ dbg_lp("LEB %d", i + c->lpt_first);
+ }
+ }
+}
+
+/**
+ * lpt_tgc_end - end trivial garbage collection of LPT LEBs.
+ * @c: UBIFS file-system description object
+ *
+ * LPT trivial garbage collection is where a LPT LEB contains only dirty and
+ * free space and so may be reused as soon as the next commit is completed.
+ * This function is called after the commit is completed (master node has been
+ * written) and un-maps LPT LEBs that were marked for trivial GC.
+ */
+static int lpt_tgc_end(struct ubifs_info *c)
+{
+ int i, err;
+
+ for (i = 0; i < c->lpt_lebs; i++)
+ if (c->ltab[i].tgc) {
+ err = ubifs_leb_unmap(c, i + c->lpt_first);
+ if (err)
+ return err;
+ c->ltab[i].tgc = 0;
+ dbg_lp("LEB %d", i + c->lpt_first);
+ }
+ return 0;
+}
+
+/**
+ * populate_lsave - fill the lsave array with important LEB numbers.
+ * @c: the UBIFS file-system description object
+ *
+ * This function is only called for the "big" model. It records a small number
+ * of LEB numbers of important LEBs. Important LEBs are ones that are (from
+ * most important to least important): empty, freeable, freeable index, dirty
+ * index, dirty or free. Upon mount, we read this list of LEB numbers and bring
+ * their pnodes into memory. That will stop us from having to scan the LPT
+ * straight away. For the "small" model we assume that scanning the LPT is no
+ * big deal.
+ */
+static void populate_lsave(struct ubifs_info *c)
+{
+ struct ubifs_lprops *lprops;
+ struct ubifs_lpt_heap *heap;
+ int i, cnt = 0;
+
+ ubifs_assert(c->big_lpt);
+ if (!(c->lpt_drty_flgs & LSAVE_DIRTY)) {
+ c->lpt_drty_flgs |= LSAVE_DIRTY;
+ ubifs_add_lpt_dirt(c, c->lsave_lnum, c->lsave_sz);
+ }
+
+#ifndef __UBOOT__
+ if (dbg_populate_lsave(c))
+ return;
+#endif
+
+ list_for_each_entry(lprops, &c->empty_list, list) {
+ c->lsave[cnt++] = lprops->lnum;
+ if (cnt >= c->lsave_cnt)
+ return;
+ }
+ list_for_each_entry(lprops, &c->freeable_list, list) {
+ c->lsave[cnt++] = lprops->lnum;
+ if (cnt >= c->lsave_cnt)
+ return;
+ }
+ list_for_each_entry(lprops, &c->frdi_idx_list, list) {
+ c->lsave[cnt++] = lprops->lnum;
+ if (cnt >= c->lsave_cnt)
+ return;
+ }
+ heap = &c->lpt_heap[LPROPS_DIRTY_IDX - 1];
+ for (i = 0; i < heap->cnt; i++) {
+ c->lsave[cnt++] = heap->arr[i]->lnum;
+ if (cnt >= c->lsave_cnt)
+ return;
+ }
+ heap = &c->lpt_heap[LPROPS_DIRTY - 1];
+ for (i = 0; i < heap->cnt; i++) {
+ c->lsave[cnt++] = heap->arr[i]->lnum;
+ if (cnt >= c->lsave_cnt)
+ return;
+ }
+ heap = &c->lpt_heap[LPROPS_FREE - 1];
+ for (i = 0; i < heap->cnt; i++) {
+ c->lsave[cnt++] = heap->arr[i]->lnum;
+ if (cnt >= c->lsave_cnt)
+ return;
+ }
+ /* Fill it up completely */
+ while (cnt < c->lsave_cnt)
+ c->lsave[cnt++] = c->main_first;
+}
+
+/**
+ * nnode_lookup - lookup a nnode in the LPT.
+ * @c: UBIFS file-system description object
+ * @i: nnode number
+ *
+ * This function returns a pointer to the nnode on success or a negative
+ * error code on failure.
+ */
+static struct ubifs_nnode *nnode_lookup(struct ubifs_info *c, int i)
+{
+ int err, iip;
+ struct ubifs_nnode *nnode;
+
+ if (!c->nroot) {
+ err = ubifs_read_nnode(c, NULL, 0);
+ if (err)
+ return ERR_PTR(err);
+ }
+ nnode = c->nroot;
+ while (1) {
+ iip = i & (UBIFS_LPT_FANOUT - 1);
+ i >>= UBIFS_LPT_FANOUT_SHIFT;
+ if (!i)
+ break;
+ nnode = ubifs_get_nnode(c, nnode, iip);
+ if (IS_ERR(nnode))
+ return nnode;
+ }
+ return nnode;
+}
+
+/**
+ * make_nnode_dirty - find a nnode and, if found, make it dirty.
+ * @c: UBIFS file-system description object
+ * @node_num: nnode number of nnode to make dirty
+ * @lnum: LEB number where nnode was written
+ * @offs: offset where nnode was written
+ *
+ * This function is used by LPT garbage collection. LPT garbage collection is
+ * used only for the "big" LPT model (c->big_lpt == 1). Garbage collection
+ * simply involves marking all the nodes in the LEB being garbage-collected as
+ * dirty. The dirty nodes are written next commit, after which the LEB is free
+ * to be reused.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int make_nnode_dirty(struct ubifs_info *c, int node_num, int lnum,
+ int offs)
+{
+ struct ubifs_nnode *nnode;
+
+ nnode = nnode_lookup(c, node_num);
+ if (IS_ERR(nnode))
+ return PTR_ERR(nnode);
+ if (nnode->parent) {
+ struct ubifs_nbranch *branch;
+
+ branch = &nnode->parent->nbranch[nnode->iip];
+ if (branch->lnum != lnum || branch->offs != offs)
+ return 0; /* nnode is obsolete */
+ } else if (c->lpt_lnum != lnum || c->lpt_offs != offs)
+ return 0; /* nnode is obsolete */
+ /* Assumes cnext list is empty i.e. not called during commit */
+ if (!test_and_set_bit(DIRTY_CNODE, &nnode->flags)) {
+ c->dirty_nn_cnt += 1;
+ ubifs_add_nnode_dirt(c, nnode);
+ /* Mark parent and ancestors dirty too */
+ nnode = nnode->parent;
+ while (nnode) {
+ if (!test_and_set_bit(DIRTY_CNODE, &nnode->flags)) {
+ c->dirty_nn_cnt += 1;
+ ubifs_add_nnode_dirt(c, nnode);
+ nnode = nnode->parent;
+ } else
+ break;
+ }
+ }
+ return 0;
+}
+
+/**
+ * make_pnode_dirty - find a pnode and, if found, make it dirty.
+ * @c: UBIFS file-system description object
+ * @node_num: pnode number of pnode to make dirty
+ * @lnum: LEB number where pnode was written
+ * @offs: offset where pnode was written
+ *
+ * This function is used by LPT garbage collection. LPT garbage collection is
+ * used only for the "big" LPT model (c->big_lpt == 1). Garbage collection
+ * simply involves marking all the nodes in the LEB being garbage-collected as
+ * dirty. The dirty nodes are written next commit, after which the LEB is free
+ * to be reused.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int make_pnode_dirty(struct ubifs_info *c, int node_num, int lnum,
+ int offs)
+{
+ struct ubifs_pnode *pnode;
+ struct ubifs_nbranch *branch;
+
+ pnode = pnode_lookup(c, node_num);
+ if (IS_ERR(pnode))
+ return PTR_ERR(pnode);
+ branch = &pnode->parent->nbranch[pnode->iip];
+ if (branch->lnum != lnum || branch->offs != offs)
+ return 0;
+ do_make_pnode_dirty(c, pnode);
+ return 0;
+}
+
+/**
+ * make_ltab_dirty - make ltab node dirty.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number where ltab was written
+ * @offs: offset where ltab was written
+ *
+ * This function is used by LPT garbage collection. LPT garbage collection is
+ * used only for the "big" LPT model (c->big_lpt == 1). Garbage collection
+ * simply involves marking all the nodes in the LEB being garbage-collected as
+ * dirty. The dirty nodes are written next commit, after which the LEB is free
+ * to be reused.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int make_ltab_dirty(struct ubifs_info *c, int lnum, int offs)
+{
+ if (lnum != c->ltab_lnum || offs != c->ltab_offs)
+ return 0; /* This ltab node is obsolete */
+ if (!(c->lpt_drty_flgs & LTAB_DIRTY)) {
+ c->lpt_drty_flgs |= LTAB_DIRTY;
+ ubifs_add_lpt_dirt(c, c->ltab_lnum, c->ltab_sz);
+ }
+ return 0;
+}
+
+/**
+ * make_lsave_dirty - make lsave node dirty.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number where lsave was written
+ * @offs: offset where lsave was written
+ *
+ * This function is used by LPT garbage collection. LPT garbage collection is
+ * used only for the "big" LPT model (c->big_lpt == 1). Garbage collection
+ * simply involves marking all the nodes in the LEB being garbage-collected as
+ * dirty. The dirty nodes are written next commit, after which the LEB is free
+ * to be reused.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int make_lsave_dirty(struct ubifs_info *c, int lnum, int offs)
+{
+ if (lnum != c->lsave_lnum || offs != c->lsave_offs)
+ return 0; /* This lsave node is obsolete */
+ if (!(c->lpt_drty_flgs & LSAVE_DIRTY)) {
+ c->lpt_drty_flgs |= LSAVE_DIRTY;
+ ubifs_add_lpt_dirt(c, c->lsave_lnum, c->lsave_sz);
+ }
+ return 0;
+}
+
+/**
+ * make_node_dirty - make node dirty.
+ * @c: UBIFS file-system description object
+ * @node_type: LPT node type
+ * @node_num: node number
+ * @lnum: LEB number where node was written
+ * @offs: offset where node was written
+ *
+ * This function is used by LPT garbage collection. LPT garbage collection is
+ * used only for the "big" LPT model (c->big_lpt == 1). Garbage collection
+ * simply involves marking all the nodes in the LEB being garbage-collected as
+ * dirty. The dirty nodes are written next commit, after which the LEB is free
+ * to be reused.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int make_node_dirty(struct ubifs_info *c, int node_type, int node_num,
+ int lnum, int offs)
+{
+ switch (node_type) {
+ case UBIFS_LPT_NNODE:
+ return make_nnode_dirty(c, node_num, lnum, offs);
+ case UBIFS_LPT_PNODE:
+ return make_pnode_dirty(c, node_num, lnum, offs);
+ case UBIFS_LPT_LTAB:
+ return make_ltab_dirty(c, lnum, offs);
+ case UBIFS_LPT_LSAVE:
+ return make_lsave_dirty(c, lnum, offs);
+ }
+ return -EINVAL;
+}
+
+/**
+ * get_lpt_node_len - return the length of a node based on its type.
+ * @c: UBIFS file-system description object
+ * @node_type: LPT node type
+ */
+static int get_lpt_node_len(const struct ubifs_info *c, int node_type)
+{
+ switch (node_type) {
+ case UBIFS_LPT_NNODE:
+ return c->nnode_sz;
+ case UBIFS_LPT_PNODE:
+ return c->pnode_sz;
+ case UBIFS_LPT_LTAB:
+ return c->ltab_sz;
+ case UBIFS_LPT_LSAVE:
+ return c->lsave_sz;
+ }
+ return 0;
+}
+
+/**
+ * get_pad_len - return the length of padding in a buffer.
+ * @c: UBIFS file-system description object
+ * @buf: buffer
+ * @len: length of buffer
+ */
+static int get_pad_len(const struct ubifs_info *c, uint8_t *buf, int len)
+{
+ int offs, pad_len;
+
+ if (c->min_io_size == 1)
+ return 0;
+ offs = c->leb_size - len;
+ pad_len = ALIGN(offs, c->min_io_size) - offs;
+ return pad_len;
+}
+
+/**
+ * get_lpt_node_type - return type (and node number) of a node in a buffer.
+ * @c: UBIFS file-system description object
+ * @buf: buffer
+ * @node_num: node number is returned here
+ */
+static int get_lpt_node_type(const struct ubifs_info *c, uint8_t *buf,
+ int *node_num)
+{
+ uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES;
+ int pos = 0, node_type;
+
+ node_type = ubifs_unpack_bits(&addr, &pos, UBIFS_LPT_TYPE_BITS);
+ *node_num = ubifs_unpack_bits(&addr, &pos, c->pcnt_bits);
+ return node_type;
+}
+
+/**
+ * is_a_node - determine if a buffer contains a node.
+ * @c: UBIFS file-system description object
+ * @buf: buffer
+ * @len: length of buffer
+ *
+ * This function returns %1 if the buffer contains a node or %0 if it does not.
+ */
+static int is_a_node(const struct ubifs_info *c, uint8_t *buf, int len)
+{
+ uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES;
+ int pos = 0, node_type, node_len;
+ uint16_t crc, calc_crc;
+
+ if (len < UBIFS_LPT_CRC_BYTES + (UBIFS_LPT_TYPE_BITS + 7) / 8)
+ return 0;
+ node_type = ubifs_unpack_bits(&addr, &pos, UBIFS_LPT_TYPE_BITS);
+ if (node_type == UBIFS_LPT_NOT_A_NODE)
+ return 0;
+ node_len = get_lpt_node_len(c, node_type);
+ if (!node_len || node_len > len)
+ return 0;
+ pos = 0;
+ addr = buf;
+ crc = ubifs_unpack_bits(&addr, &pos, UBIFS_LPT_CRC_BITS);
+ calc_crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES,
+ node_len - UBIFS_LPT_CRC_BYTES);
+ if (crc != calc_crc)
+ return 0;
+ return 1;
+}
+
+/**
+ * lpt_gc_lnum - garbage collect a LPT LEB.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number to garbage collect
+ *
+ * LPT garbage collection is used only for the "big" LPT model
+ * (c->big_lpt == 1). Garbage collection simply involves marking all the nodes
+ * in the LEB being garbage-collected as dirty. The dirty nodes are written
+ * next commit, after which the LEB is free to be reused.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int lpt_gc_lnum(struct ubifs_info *c, int lnum)
+{
+ int err, len = c->leb_size, node_type, node_num, node_len, offs;
+ void *buf = c->lpt_buf;
+
+ dbg_lp("LEB %d", lnum);
+
+ err = ubifs_leb_read(c, lnum, buf, 0, c->leb_size, 1);
+ if (err)
+ return err;
+
+ while (1) {
+ if (!is_a_node(c, buf, len)) {
+ int pad_len;
+
+ pad_len = get_pad_len(c, buf, len);
+ if (pad_len) {
+ buf += pad_len;
+ len -= pad_len;
+ continue;
+ }
+ return 0;
+ }
+ node_type = get_lpt_node_type(c, buf, &node_num);
+ node_len = get_lpt_node_len(c, node_type);
+ offs = c->leb_size - len;
+ ubifs_assert(node_len != 0);
+ mutex_lock(&c->lp_mutex);
+ err = make_node_dirty(c, node_type, node_num, lnum, offs);
+ mutex_unlock(&c->lp_mutex);
+ if (err)
+ return err;
+ buf += node_len;
+ len -= node_len;
+ }
+ return 0;
+}
+
+/**
+ * lpt_gc - LPT garbage collection.
+ * @c: UBIFS file-system description object
+ *
+ * Select a LPT LEB for LPT garbage collection and call 'lpt_gc_lnum()'.
+ * Returns %0 on success and a negative error code on failure.
+ */
+static int lpt_gc(struct ubifs_info *c)
+{
+ int i, lnum = -1, dirty = 0;
+
+ mutex_lock(&c->lp_mutex);
+ for (i = 0; i < c->lpt_lebs; i++) {
+ ubifs_assert(!c->ltab[i].tgc);
+ if (i + c->lpt_first == c->nhead_lnum ||
+ c->ltab[i].free + c->ltab[i].dirty == c->leb_size)
+ continue;
+ if (c->ltab[i].dirty > dirty) {
+ dirty = c->ltab[i].dirty;
+ lnum = i + c->lpt_first;
+ }
+ }
+ mutex_unlock(&c->lp_mutex);
+ if (lnum == -1)
+ return -ENOSPC;
+ return lpt_gc_lnum(c, lnum);
+}
+
+/**
+ * ubifs_lpt_start_commit - UBIFS commit starts.
+ * @c: the UBIFS file-system description object
+ *
+ * This function has to be called when UBIFS starts the commit operation.
+ * This function "freezes" all currently dirty LEB properties and does not
+ * change them anymore. Further changes are saved and tracked separately
+ * because they are not part of this commit. This function returns zero in case
+ * of success and a negative error code in case of failure.
+ */
+int ubifs_lpt_start_commit(struct ubifs_info *c)
+{
+ int err, cnt;
+
+ dbg_lp("");
+
+ mutex_lock(&c->lp_mutex);
+ err = dbg_chk_lpt_free_spc(c);
+ if (err)
+ goto out;
+ err = dbg_check_ltab(c);
+ if (err)
+ goto out;
+
+ if (c->check_lpt_free) {
+ /*
+ * We ensure there is enough free space in
+ * ubifs_lpt_post_commit() by marking nodes dirty. That
+ * information is lost when we unmount, so we also need
+ * to check free space once after mounting also.
+ */
+ c->check_lpt_free = 0;
+ while (need_write_all(c)) {
+ mutex_unlock(&c->lp_mutex);
+ err = lpt_gc(c);
+ if (err)
+ return err;
+ mutex_lock(&c->lp_mutex);
+ }
+ }
+
+ lpt_tgc_start(c);
+
+ if (!c->dirty_pn_cnt) {
+ dbg_cmt("no cnodes to commit");
+ err = 0;
+ goto out;
+ }
+
+ if (!c->big_lpt && need_write_all(c)) {
+ /* If needed, write everything */
+ err = make_tree_dirty(c);
+ if (err)
+ goto out;
+ lpt_tgc_start(c);
+ }
+
+ if (c->big_lpt)
+ populate_lsave(c);
+
+ cnt = get_cnodes_to_commit(c);
+ ubifs_assert(cnt != 0);
+
+ err = layout_cnodes(c);
+ if (err)
+ goto out;
+
+ /* Copy the LPT's own lprops for end commit to write */
+ memcpy(c->ltab_cmt, c->ltab,
+ sizeof(struct ubifs_lpt_lprops) * c->lpt_lebs);
+ c->lpt_drty_flgs &= ~(LTAB_DIRTY | LSAVE_DIRTY);
+
+out:
+ mutex_unlock(&c->lp_mutex);
+ return err;
+}
+
+/**
+ * free_obsolete_cnodes - free obsolete cnodes for commit end.
+ * @c: UBIFS file-system description object
+ */
+static void free_obsolete_cnodes(struct ubifs_info *c)
+{
+ struct ubifs_cnode *cnode, *cnext;
+
+ cnext = c->lpt_cnext;
+ if (!cnext)
+ return;
+ do {
+ cnode = cnext;
+ cnext = cnode->cnext;
+ if (test_bit(OBSOLETE_CNODE, &cnode->flags))
+ kfree(cnode);
+ else
+ cnode->cnext = NULL;
+ } while (cnext != c->lpt_cnext);
+ c->lpt_cnext = NULL;
+}
+
+#ifndef __UBOOT__
+/**
+ * ubifs_lpt_end_commit - finish the commit operation.
+ * @c: the UBIFS file-system description object
+ *
+ * This function has to be called when the commit operation finishes. It
+ * flushes the changes which were "frozen" by 'ubifs_lprops_start_commit()' to
+ * the media. Returns zero in case of success and a negative error code in case
+ * of failure.
+ */
+int ubifs_lpt_end_commit(struct ubifs_info *c)
+{
+ int err;
+
+ dbg_lp("");
+
+ if (!c->lpt_cnext)
+ return 0;
+
+ err = write_cnodes(c);
+ if (err)
+ return err;
+
+ mutex_lock(&c->lp_mutex);
+ free_obsolete_cnodes(c);
+ mutex_unlock(&c->lp_mutex);
+
+ return 0;
+}
+#endif
+
+/**
+ * ubifs_lpt_post_commit - post commit LPT trivial GC and LPT GC.
+ * @c: UBIFS file-system description object
+ *
+ * LPT trivial GC is completed after a commit. Also LPT GC is done after a
+ * commit for the "big" LPT model.
+ */
+int ubifs_lpt_post_commit(struct ubifs_info *c)
+{
+ int err;
+
+ mutex_lock(&c->lp_mutex);
+ err = lpt_tgc_end(c);
+ if (err)
+ goto out;
+ if (c->big_lpt)
+ while (need_write_all(c)) {
+ mutex_unlock(&c->lp_mutex);
+ err = lpt_gc(c);
+ if (err)
+ return err;
+ mutex_lock(&c->lp_mutex);
+ }
+out:
+ mutex_unlock(&c->lp_mutex);
+ return err;
+}
+
+/**
+ * first_nnode - find the first nnode in memory.
+ * @c: UBIFS file-system description object
+ * @hght: height of tree where nnode found is returned here
+ *
+ * This function returns a pointer to the nnode found or %NULL if no nnode is
+ * found. This function is a helper to 'ubifs_lpt_free()'.
+ */
+static struct ubifs_nnode *first_nnode(struct ubifs_info *c, int *hght)
+{
+ struct ubifs_nnode *nnode;
+ int h, i, found;
+
+ nnode = c->nroot;
+ *hght = 0;
+ if (!nnode)
+ return NULL;
+ for (h = 1; h < c->lpt_hght; h++) {
+ found = 0;
+ for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
+ if (nnode->nbranch[i].nnode) {
+ found = 1;
+ nnode = nnode->nbranch[i].nnode;
+ *hght = h;
+ break;
+ }
+ }
+ if (!found)
+ break;
+ }
+ return nnode;
+}
+
+/**
+ * next_nnode - find the next nnode in memory.
+ * @c: UBIFS file-system description object
+ * @nnode: nnode from which to start.
+ * @hght: height of tree where nnode is, is passed and returned here
+ *
+ * This function returns a pointer to the nnode found or %NULL if no nnode is
+ * found. This function is a helper to 'ubifs_lpt_free()'.
+ */
+static struct ubifs_nnode *next_nnode(struct ubifs_info *c,
+ struct ubifs_nnode *nnode, int *hght)
+{
+ struct ubifs_nnode *parent;
+ int iip, h, i, found;
+
+ parent = nnode->parent;
+ if (!parent)
+ return NULL;
+ if (nnode->iip == UBIFS_LPT_FANOUT - 1) {
+ *hght -= 1;
+ return parent;
+ }
+ for (iip = nnode->iip + 1; iip < UBIFS_LPT_FANOUT; iip++) {
+ nnode = parent->nbranch[iip].nnode;
+ if (nnode)
+ break;
+ }
+ if (!nnode) {
+ *hght -= 1;
+ return parent;
+ }
+ for (h = *hght + 1; h < c->lpt_hght; h++) {
+ found = 0;
+ for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
+ if (nnode->nbranch[i].nnode) {
+ found = 1;
+ nnode = nnode->nbranch[i].nnode;
+ *hght = h;
+ break;
+ }
+ }
+ if (!found)
+ break;
+ }
+ return nnode;
+}
+
+/**
+ * ubifs_lpt_free - free resources owned by the LPT.
+ * @c: UBIFS file-system description object
+ * @wr_only: free only resources used for writing
+ */
+void ubifs_lpt_free(struct ubifs_info *c, int wr_only)
+{
+ struct ubifs_nnode *nnode;
+ int i, hght;
+
+ /* Free write-only things first */
+
+ free_obsolete_cnodes(c); /* Leftover from a failed commit */
+
+ vfree(c->ltab_cmt);
+ c->ltab_cmt = NULL;
+ vfree(c->lpt_buf);
+ c->lpt_buf = NULL;
+ kfree(c->lsave);
+ c->lsave = NULL;
+
+ if (wr_only)
+ return;
+
+ /* Now free the rest */
+
+ nnode = first_nnode(c, &hght);
+ while (nnode) {
+ for (i = 0; i < UBIFS_LPT_FANOUT; i++)
+ kfree(nnode->nbranch[i].nnode);
+ nnode = next_nnode(c, nnode, &hght);
+ }
+ for (i = 0; i < LPROPS_HEAP_CNT; i++)
+ kfree(c->lpt_heap[i].arr);
+ kfree(c->dirty_idx.arr);
+ kfree(c->nroot);
+ vfree(c->ltab);
+ kfree(c->lpt_nod_buf);
+}
+
+#ifndef __UBOOT__
+/*
+ * Everything below is related to debugging.
+ */
+
+/**
+ * dbg_is_all_ff - determine if a buffer contains only 0xFF bytes.
+ * @buf: buffer
+ * @len: buffer length
+ */
+static int dbg_is_all_ff(uint8_t *buf, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ if (buf[i] != 0xff)
+ return 0;
+ return 1;
+}
+
+/**
+ * dbg_is_nnode_dirty - determine if a nnode is dirty.
+ * @c: the UBIFS file-system description object
+ * @lnum: LEB number where nnode was written
+ * @offs: offset where nnode was written
+ */
+static int dbg_is_nnode_dirty(struct ubifs_info *c, int lnum, int offs)
+{
+ struct ubifs_nnode *nnode;
+ int hght;
+
+ /* Entire tree is in memory so first_nnode / next_nnode are OK */
+ nnode = first_nnode(c, &hght);
+ for (; nnode; nnode = next_nnode(c, nnode, &hght)) {
+ struct ubifs_nbranch *branch;
+
+ cond_resched();
+ if (nnode->parent) {
+ branch = &nnode->parent->nbranch[nnode->iip];
+ if (branch->lnum != lnum || branch->offs != offs)
+ continue;
+ if (test_bit(DIRTY_CNODE, &nnode->flags))
+ return 1;
+ return 0;
+ } else {
+ if (c->lpt_lnum != lnum || c->lpt_offs != offs)
+ continue;
+ if (test_bit(DIRTY_CNODE, &nnode->flags))
+ return 1;
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/**
+ * dbg_is_pnode_dirty - determine if a pnode is dirty.
+ * @c: the UBIFS file-system description object
+ * @lnum: LEB number where pnode was written
+ * @offs: offset where pnode was written
+ */
+static int dbg_is_pnode_dirty(struct ubifs_info *c, int lnum, int offs)
+{
+ int i, cnt;
+
+ cnt = DIV_ROUND_UP(c->main_lebs, UBIFS_LPT_FANOUT);
+ for (i = 0; i < cnt; i++) {
+ struct ubifs_pnode *pnode;
+ struct ubifs_nbranch *branch;
+
+ cond_resched();
+ pnode = pnode_lookup(c, i);
+ if (IS_ERR(pnode))
+ return PTR_ERR(pnode);
+ branch = &pnode->parent->nbranch[pnode->iip];
+ if (branch->lnum != lnum || branch->offs != offs)
+ continue;
+ if (test_bit(DIRTY_CNODE, &pnode->flags))
+ return 1;
+ return 0;
+ }
+ return 1;
+}
+
+/**
+ * dbg_is_ltab_dirty - determine if a ltab node is dirty.
+ * @c: the UBIFS file-system description object
+ * @lnum: LEB number where ltab node was written
+ * @offs: offset where ltab node was written
+ */
+static int dbg_is_ltab_dirty(struct ubifs_info *c, int lnum, int offs)
+{
+ if (lnum != c->ltab_lnum || offs != c->ltab_offs)
+ return 1;
+ return (c->lpt_drty_flgs & LTAB_DIRTY) != 0;
+}
+
+/**
+ * dbg_is_lsave_dirty - determine if a lsave node is dirty.
+ * @c: the UBIFS file-system description object
+ * @lnum: LEB number where lsave node was written
+ * @offs: offset where lsave node was written
+ */
+static int dbg_is_lsave_dirty(struct ubifs_info *c, int lnum, int offs)
+{
+ if (lnum != c->lsave_lnum || offs != c->lsave_offs)
+ return 1;
+ return (c->lpt_drty_flgs & LSAVE_DIRTY) != 0;
+}
+
+/**
+ * dbg_is_node_dirty - determine if a node is dirty.
+ * @c: the UBIFS file-system description object
+ * @node_type: node type
+ * @lnum: LEB number where node was written
+ * @offs: offset where node was written
+ */
+static int dbg_is_node_dirty(struct ubifs_info *c, int node_type, int lnum,
+ int offs)
+{
+ switch (node_type) {
+ case UBIFS_LPT_NNODE:
+ return dbg_is_nnode_dirty(c, lnum, offs);
+ case UBIFS_LPT_PNODE:
+ return dbg_is_pnode_dirty(c, lnum, offs);
+ case UBIFS_LPT_LTAB:
+ return dbg_is_ltab_dirty(c, lnum, offs);
+ case UBIFS_LPT_LSAVE:
+ return dbg_is_lsave_dirty(c, lnum, offs);
+ }
+ return 1;
+}
+
+/**
+ * dbg_check_ltab_lnum - check the ltab for a LPT LEB number.
+ * @c: the UBIFS file-system description object
+ * @lnum: LEB number where node was written
+ * @offs: offset where node was written
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int dbg_check_ltab_lnum(struct ubifs_info *c, int lnum)
+{
+ int err, len = c->leb_size, dirty = 0, node_type, node_num, node_len;
+ int ret;
+ void *buf, *p;
+
+ if (!dbg_is_chk_lprops(c))
+ return 0;
+
+ buf = p = __vmalloc(c->leb_size, GFP_NOFS, PAGE_KERNEL);
+ if (!buf) {
+ ubifs_err(c, "cannot allocate memory for ltab checking");
+ return 0;
+ }
+
+ dbg_lp("LEB %d", lnum);
+
+ err = ubifs_leb_read(c, lnum, buf, 0, c->leb_size, 1);
+ if (err)
+ goto out;
+
+ while (1) {
+ if (!is_a_node(c, p, len)) {
+ int i, pad_len;
+
+ pad_len = get_pad_len(c, p, len);
+ if (pad_len) {
+ p += pad_len;
+ len -= pad_len;
+ dirty += pad_len;
+ continue;
+ }
+ if (!dbg_is_all_ff(p, len)) {
+ ubifs_err(c, "invalid empty space in LEB %d at %d",
+ lnum, c->leb_size - len);
+ err = -EINVAL;
+ }
+ i = lnum - c->lpt_first;
+ if (len != c->ltab[i].free) {
+ ubifs_err(c, "invalid free space in LEB %d (free %d, expected %d)",
+ lnum, len, c->ltab[i].free);
+ err = -EINVAL;
+ }
+ if (dirty != c->ltab[i].dirty) {
+ ubifs_err(c, "invalid dirty space in LEB %d (dirty %d, expected %d)",
+ lnum, dirty, c->ltab[i].dirty);
+ err = -EINVAL;
+ }
+ goto out;
+ }
+ node_type = get_lpt_node_type(c, p, &node_num);
+ node_len = get_lpt_node_len(c, node_type);
+ ret = dbg_is_node_dirty(c, node_type, lnum, c->leb_size - len);
+ if (ret == 1)
+ dirty += node_len;
+ p += node_len;
+ len -= node_len;
+ }
+
+ err = 0;
+out:
+ vfree(buf);
+ return err;
+}
+
+/**
+ * dbg_check_ltab - check the free and dirty space in the ltab.
+ * @c: the UBIFS file-system description object
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int dbg_check_ltab(struct ubifs_info *c)
+{
+ int lnum, err, i, cnt;
+
+ if (!dbg_is_chk_lprops(c))
+ return 0;
+
+ /* Bring the entire tree into memory */
+ cnt = DIV_ROUND_UP(c->main_lebs, UBIFS_LPT_FANOUT);
+ for (i = 0; i < cnt; i++) {
+ struct ubifs_pnode *pnode;
+
+ pnode = pnode_lookup(c, i);
+ if (IS_ERR(pnode))
+ return PTR_ERR(pnode);
+ cond_resched();
+ }
+
+ /* Check nodes */
+ err = dbg_check_lpt_nodes(c, (struct ubifs_cnode *)c->nroot, 0, 0);
+ if (err)
+ return err;
+
+ /* Check each LEB */
+ for (lnum = c->lpt_first; lnum <= c->lpt_last; lnum++) {
+ err = dbg_check_ltab_lnum(c, lnum);
+ if (err) {
+ ubifs_err(c, "failed at LEB %d", lnum);
+ return err;
+ }
+ }
+
+ dbg_lp("succeeded");
+ return 0;
+}
+
+/**
+ * dbg_chk_lpt_free_spc - check LPT free space is enough to write entire LPT.
+ * @c: the UBIFS file-system description object
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int dbg_chk_lpt_free_spc(struct ubifs_info *c)
+{
+ long long free = 0;
+ int i;
+
+ if (!dbg_is_chk_lprops(c))
+ return 0;
+
+ for (i = 0; i < c->lpt_lebs; i++) {
+ if (c->ltab[i].tgc || c->ltab[i].cmt)
+ continue;
+ if (i + c->lpt_first == c->nhead_lnum)
+ free += c->leb_size - c->nhead_offs;
+ else if (c->ltab[i].free == c->leb_size)
+ free += c->leb_size;
+ }
+ if (free < c->lpt_sz) {
+ ubifs_err(c, "LPT space error: free %lld lpt_sz %lld",
+ free, c->lpt_sz);
+ ubifs_dump_lpt_info(c);
+ ubifs_dump_lpt_lebs(c);
+ dump_stack();
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/**
+ * dbg_chk_lpt_sz - check LPT does not write more than LPT size.
+ * @c: the UBIFS file-system description object
+ * @action: what to do
+ * @len: length written
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ * The @action argument may be one of:
+ * o %0 - LPT debugging checking starts, initialize debugging variables;
+ * o %1 - wrote an LPT node, increase LPT size by @len bytes;
+ * o %2 - switched to a different LEB and wasted @len bytes;
+ * o %3 - check that we've written the right number of bytes.
+ * o %4 - wasted @len bytes;
+ */
+int dbg_chk_lpt_sz(struct ubifs_info *c, int action, int len)
+{
+ struct ubifs_debug_info *d = c->dbg;
+ long long chk_lpt_sz, lpt_sz;
+ int err = 0;
+
+ if (!dbg_is_chk_lprops(c))
+ return 0;
+
+ switch (action) {
+ case 0:
+ d->chk_lpt_sz = 0;
+ d->chk_lpt_sz2 = 0;
+ d->chk_lpt_lebs = 0;
+ d->chk_lpt_wastage = 0;
+ if (c->dirty_pn_cnt > c->pnode_cnt) {
+ ubifs_err(c, "dirty pnodes %d exceed max %d",
+ c->dirty_pn_cnt, c->pnode_cnt);
+ err = -EINVAL;
+ }
+ if (c->dirty_nn_cnt > c->nnode_cnt) {
+ ubifs_err(c, "dirty nnodes %d exceed max %d",
+ c->dirty_nn_cnt, c->nnode_cnt);
+ err = -EINVAL;
+ }
+ return err;
+ case 1:
+ d->chk_lpt_sz += len;
+ return 0;
+ case 2:
+ d->chk_lpt_sz += len;
+ d->chk_lpt_wastage += len;
+ d->chk_lpt_lebs += 1;
+ return 0;
+ case 3:
+ chk_lpt_sz = c->leb_size;
+ chk_lpt_sz *= d->chk_lpt_lebs;
+ chk_lpt_sz += len - c->nhead_offs;
+ if (d->chk_lpt_sz != chk_lpt_sz) {
+ ubifs_err(c, "LPT wrote %lld but space used was %lld",
+ d->chk_lpt_sz, chk_lpt_sz);
+ err = -EINVAL;
+ }
+ if (d->chk_lpt_sz > c->lpt_sz) {
+ ubifs_err(c, "LPT wrote %lld but lpt_sz is %lld",
+ d->chk_lpt_sz, c->lpt_sz);
+ err = -EINVAL;
+ }
+ if (d->chk_lpt_sz2 && d->chk_lpt_sz != d->chk_lpt_sz2) {
+ ubifs_err(c, "LPT layout size %lld but wrote %lld",
+ d->chk_lpt_sz, d->chk_lpt_sz2);
+ err = -EINVAL;
+ }
+ if (d->chk_lpt_sz2 && d->new_nhead_offs != len) {
+ ubifs_err(c, "LPT new nhead offs: expected %d was %d",
+ d->new_nhead_offs, len);
+ err = -EINVAL;
+ }
+ lpt_sz = (long long)c->pnode_cnt * c->pnode_sz;
+ lpt_sz += (long long)c->nnode_cnt * c->nnode_sz;
+ lpt_sz += c->ltab_sz;
+ if (c->big_lpt)
+ lpt_sz += c->lsave_sz;
+ if (d->chk_lpt_sz - d->chk_lpt_wastage > lpt_sz) {
+ ubifs_err(c, "LPT chk_lpt_sz %lld + waste %lld exceeds %lld",
+ d->chk_lpt_sz, d->chk_lpt_wastage, lpt_sz);
+ err = -EINVAL;
+ }
+ if (err) {
+ ubifs_dump_lpt_info(c);
+ ubifs_dump_lpt_lebs(c);
+ dump_stack();
+ }
+ d->chk_lpt_sz2 = d->chk_lpt_sz;
+ d->chk_lpt_sz = 0;
+ d->chk_lpt_wastage = 0;
+ d->chk_lpt_lebs = 0;
+ d->new_nhead_offs = len;
+ return err;
+ case 4:
+ d->chk_lpt_sz += len;
+ d->chk_lpt_wastage += len;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+/**
+ * ubifs_dump_lpt_leb - dump an LPT LEB.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number to dump
+ *
+ * This function dumps an LEB from LPT area. Nodes in this area are very
+ * different to nodes in the main area (e.g., they do not have common headers,
+ * they do not have 8-byte alignments, etc), so we have a separate function to
+ * dump LPT area LEBs. Note, LPT has to be locked by the caller.
+ */
+static void dump_lpt_leb(const struct ubifs_info *c, int lnum)
+{
+ int err, len = c->leb_size, node_type, node_num, node_len, offs;
+ void *buf, *p;
+
+ pr_err("(pid %d) start dumping LEB %d\n", current->pid, lnum);
+ buf = p = __vmalloc(c->leb_size, GFP_NOFS, PAGE_KERNEL);
+ if (!buf) {
+ ubifs_err(c, "cannot allocate memory to dump LPT");
+ return;
+ }
+
+ err = ubifs_leb_read(c, lnum, buf, 0, c->leb_size, 1);
+ if (err)
+ goto out;
+
+ while (1) {
+ offs = c->leb_size - len;
+ if (!is_a_node(c, p, len)) {
+ int pad_len;
+
+ pad_len = get_pad_len(c, p, len);
+ if (pad_len) {
+ pr_err("LEB %d:%d, pad %d bytes\n",
+ lnum, offs, pad_len);
+ p += pad_len;
+ len -= pad_len;
+ continue;
+ }
+ if (len)
+ pr_err("LEB %d:%d, free %d bytes\n",
+ lnum, offs, len);
+ break;
+ }
+
+ node_type = get_lpt_node_type(c, p, &node_num);
+ switch (node_type) {
+ case UBIFS_LPT_PNODE:
+ {
+ node_len = c->pnode_sz;
+ if (c->big_lpt)
+ pr_err("LEB %d:%d, pnode num %d\n",
+ lnum, offs, node_num);
+ else
+ pr_err("LEB %d:%d, pnode\n", lnum, offs);
+ break;
+ }
+ case UBIFS_LPT_NNODE:
+ {
+ int i;
+ struct ubifs_nnode nnode;
+
+ node_len = c->nnode_sz;
+ if (c->big_lpt)
+ pr_err("LEB %d:%d, nnode num %d, ",
+ lnum, offs, node_num);
+ else
+ pr_err("LEB %d:%d, nnode, ",
+ lnum, offs);
+ err = ubifs_unpack_nnode(c, p, &nnode);
+ if (err) {
+ pr_err("failed to unpack_node, error %d\n",
+ err);
+ break;
+ }
+ for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
+ pr_cont("%d:%d", nnode.nbranch[i].lnum,
+ nnode.nbranch[i].offs);
+ if (i != UBIFS_LPT_FANOUT - 1)
+ pr_cont(", ");
+ }
+ pr_cont("\n");
+ break;
+ }
+ case UBIFS_LPT_LTAB:
+ node_len = c->ltab_sz;
+ pr_err("LEB %d:%d, ltab\n", lnum, offs);
+ break;
+ case UBIFS_LPT_LSAVE:
+ node_len = c->lsave_sz;
+ pr_err("LEB %d:%d, lsave len\n", lnum, offs);
+ break;
+ default:
+ ubifs_err(c, "LPT node type %d not recognized", node_type);
+ goto out;
+ }
+
+ p += node_len;
+ len -= node_len;
+ }
+
+ pr_err("(pid %d) finish dumping LEB %d\n", current->pid, lnum);
+out:
+ vfree(buf);
+ return;
+}
+
+/**
+ * ubifs_dump_lpt_lebs - dump LPT lebs.
+ * @c: UBIFS file-system description object
+ *
+ * This function dumps all LPT LEBs. The caller has to make sure the LPT is
+ * locked.
+ */
+void ubifs_dump_lpt_lebs(const struct ubifs_info *c)
+{
+ int i;
+
+ pr_err("(pid %d) start dumping all LPT LEBs\n", current->pid);
+ for (i = 0; i < c->lpt_lebs; i++)
+ dump_lpt_leb(c, i + c->lpt_first);
+ pr_err("(pid %d) finish dumping all LPT LEBs\n", current->pid);
+}
+
+/**
+ * dbg_populate_lsave - debugging version of 'populate_lsave()'
+ * @c: UBIFS file-system description object
+ *
+ * This is a debugging version for 'populate_lsave()' which populates lsave
+ * with random LEBs instead of useful LEBs, which is good for test coverage.
+ * Returns zero if lsave has not been populated (this debugging feature is
+ * disabled) an non-zero if lsave has been populated.
+ */
+static int dbg_populate_lsave(struct ubifs_info *c)
+{
+ struct ubifs_lprops *lprops;
+ struct ubifs_lpt_heap *heap;
+ int i;
+
+ if (!dbg_is_chk_gen(c))
+ return 0;
+ if (prandom_u32() & 3)
+ return 0;
+
+ for (i = 0; i < c->lsave_cnt; i++)
+ c->lsave[i] = c->main_first;
+
+ list_for_each_entry(lprops, &c->empty_list, list)
+ c->lsave[prandom_u32() % c->lsave_cnt] = lprops->lnum;
+ list_for_each_entry(lprops, &c->freeable_list, list)
+ c->lsave[prandom_u32() % c->lsave_cnt] = lprops->lnum;
+ list_for_each_entry(lprops, &c->frdi_idx_list, list)
+ c->lsave[prandom_u32() % c->lsave_cnt] = lprops->lnum;
+
+ heap = &c->lpt_heap[LPROPS_DIRTY_IDX - 1];
+ for (i = 0; i < heap->cnt; i++)
+ c->lsave[prandom_u32() % c->lsave_cnt] = heap->arr[i]->lnum;
+ heap = &c->lpt_heap[LPROPS_DIRTY - 1];
+ for (i = 0; i < heap->cnt; i++)
+ c->lsave[prandom_u32() % c->lsave_cnt] = heap->arr[i]->lnum;
+ heap = &c->lpt_heap[LPROPS_FREE - 1];
+ for (i = 0; i < heap->cnt; i++)
+ c->lsave[prandom_u32() % c->lsave_cnt] = heap->arr[i]->lnum;
+
+ return 1;
+}
+#endif
diff --git a/roms/u-boot/fs/ubifs/master.c b/roms/u-boot/fs/ubifs/master.c
new file mode 100644
index 000000000..055c654ae
--- /dev/null
+++ b/roms/u-boot/fs/ubifs/master.c
@@ -0,0 +1,394 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ *
+ * Authors: Artem Bityutskiy (Битюцкий Артём)
+ * Adrian Hunter
+ */
+
+/* This file implements reading and writing the master node */
+
+#include "ubifs.h"
+#ifdef __UBOOT__
+#include <log.h>
+#include <dm/devres.h>
+#include <linux/compat.h>
+#include <linux/err.h>
+#include <ubi_uboot.h>
+#endif
+
+/**
+ * scan_for_master - search the valid master node.
+ * @c: UBIFS file-system description object
+ *
+ * This function scans the master node LEBs and search for the latest master
+ * node. Returns zero in case of success, %-EUCLEAN if there master area is
+ * corrupted and requires recovery, and a negative error code in case of
+ * failure.
+ */
+static int scan_for_master(struct ubifs_info *c)
+{
+ struct ubifs_scan_leb *sleb;
+ struct ubifs_scan_node *snod;
+ int lnum, offs = 0, nodes_cnt;
+
+ lnum = UBIFS_MST_LNUM;
+
+ sleb = ubifs_scan(c, lnum, 0, c->sbuf, 1);
+ if (IS_ERR(sleb))
+ return PTR_ERR(sleb);
+ nodes_cnt = sleb->nodes_cnt;
+ if (nodes_cnt > 0) {
+ snod = list_entry(sleb->nodes.prev, struct ubifs_scan_node,
+ list);
+ if (snod->type != UBIFS_MST_NODE)
+ goto out_dump;
+ memcpy(c->mst_node, snod->node, snod->len);
+ offs = snod->offs;
+ }
+ ubifs_scan_destroy(sleb);
+
+ lnum += 1;
+
+ sleb = ubifs_scan(c, lnum, 0, c->sbuf, 1);
+ if (IS_ERR(sleb))
+ return PTR_ERR(sleb);
+ if (sleb->nodes_cnt != nodes_cnt)
+ goto out;
+ if (!sleb->nodes_cnt)
+ goto out;
+ snod = list_entry(sleb->nodes.prev, struct ubifs_scan_node, list);
+ if (snod->type != UBIFS_MST_NODE)
+ goto out_dump;
+ if (snod->offs != offs)
+ goto out;
+ if (memcmp((void *)c->mst_node + UBIFS_CH_SZ,
+ (void *)snod->node + UBIFS_CH_SZ,
+ UBIFS_MST_NODE_SZ - UBIFS_CH_SZ))
+ goto out;
+ c->mst_offs = offs;
+ ubifs_scan_destroy(sleb);
+ return 0;
+
+out:
+ ubifs_scan_destroy(sleb);
+ return -EUCLEAN;
+
+out_dump:
+ ubifs_err(c, "unexpected node type %d master LEB %d:%d",
+ snod->type, lnum, snod->offs);
+ ubifs_scan_destroy(sleb);
+ return -EINVAL;
+}
+
+/**
+ * validate_master - validate master node.
+ * @c: UBIFS file-system description object
+ *
+ * This function validates data which was read from master node. Returns zero
+ * if the data is all right and %-EINVAL if not.
+ */
+static int validate_master(const struct ubifs_info *c)
+{
+ long long main_sz;
+ int err;
+
+ if (c->max_sqnum >= SQNUM_WATERMARK) {
+ err = 1;
+ goto out;
+ }
+
+ if (c->cmt_no >= c->max_sqnum) {
+ err = 2;
+ goto out;
+ }
+
+ if (c->highest_inum >= INUM_WATERMARK) {
+ err = 3;
+ goto out;
+ }
+
+ if (c->lhead_lnum < UBIFS_LOG_LNUM ||
+ c->lhead_lnum >= UBIFS_LOG_LNUM + c->log_lebs ||
+ c->lhead_offs < 0 || c->lhead_offs >= c->leb_size ||
+ c->lhead_offs & (c->min_io_size - 1)) {
+ err = 4;
+ goto out;
+ }
+
+ if (c->zroot.lnum >= c->leb_cnt || c->zroot.lnum < c->main_first ||
+ c->zroot.offs >= c->leb_size || c->zroot.offs & 7) {
+ err = 5;
+ goto out;
+ }
+
+ if (c->zroot.len < c->ranges[UBIFS_IDX_NODE].min_len ||
+ c->zroot.len > c->ranges[UBIFS_IDX_NODE].max_len) {
+ err = 6;
+ goto out;
+ }
+
+ if (c->gc_lnum >= c->leb_cnt || c->gc_lnum < c->main_first) {
+ err = 7;
+ goto out;
+ }
+
+ if (c->ihead_lnum >= c->leb_cnt || c->ihead_lnum < c->main_first ||
+ c->ihead_offs % c->min_io_size || c->ihead_offs < 0 ||
+ c->ihead_offs > c->leb_size || c->ihead_offs & 7) {
+ err = 8;
+ goto out;
+ }
+
+ main_sz = (long long)c->main_lebs * c->leb_size;
+ if (c->bi.old_idx_sz & 7 || c->bi.old_idx_sz >= main_sz) {
+ err = 9;
+ goto out;
+ }
+
+ if (c->lpt_lnum < c->lpt_first || c->lpt_lnum > c->lpt_last ||
+ c->lpt_offs < 0 || c->lpt_offs + c->nnode_sz > c->leb_size) {
+ err = 10;
+ goto out;
+ }
+
+ if (c->nhead_lnum < c->lpt_first || c->nhead_lnum > c->lpt_last ||
+ c->nhead_offs < 0 || c->nhead_offs % c->min_io_size ||
+ c->nhead_offs > c->leb_size) {
+ err = 11;
+ goto out;
+ }
+
+ if (c->ltab_lnum < c->lpt_first || c->ltab_lnum > c->lpt_last ||
+ c->ltab_offs < 0 ||
+ c->ltab_offs + c->ltab_sz > c->leb_size) {
+ err = 12;
+ goto out;
+ }
+
+ if (c->big_lpt && (c->lsave_lnum < c->lpt_first ||
+ c->lsave_lnum > c->lpt_last || c->lsave_offs < 0 ||
+ c->lsave_offs + c->lsave_sz > c->leb_size)) {
+ err = 13;
+ goto out;
+ }
+
+ if (c->lscan_lnum < c->main_first || c->lscan_lnum >= c->leb_cnt) {
+ err = 14;
+ goto out;
+ }
+
+ if (c->lst.empty_lebs < 0 || c->lst.empty_lebs > c->main_lebs - 2) {
+ err = 15;
+ goto out;
+ }
+
+ if (c->lst.idx_lebs < 0 || c->lst.idx_lebs > c->main_lebs - 1) {
+ err = 16;
+ goto out;
+ }
+
+ if (c->lst.total_free < 0 || c->lst.total_free > main_sz ||
+ c->lst.total_free & 7) {
+ err = 17;
+ goto out;
+ }
+
+ if (c->lst.total_dirty < 0 || (c->lst.total_dirty & 7)) {
+ err = 18;
+ goto out;
+ }
+
+ if (c->lst.total_used < 0 || (c->lst.total_used & 7)) {
+ err = 19;
+ goto out;
+ }
+
+ if (c->lst.total_free + c->lst.total_dirty +
+ c->lst.total_used > main_sz) {
+ err = 20;
+ goto out;
+ }
+
+ if (c->lst.total_dead + c->lst.total_dark +
+ c->lst.total_used + c->bi.old_idx_sz > main_sz) {
+ err = 21;
+ goto out;
+ }
+
+ if (c->lst.total_dead < 0 ||
+ c->lst.total_dead > c->lst.total_free + c->lst.total_dirty ||
+ c->lst.total_dead & 7) {
+ err = 22;
+ goto out;
+ }
+
+ if (c->lst.total_dark < 0 ||
+ c->lst.total_dark > c->lst.total_free + c->lst.total_dirty ||
+ c->lst.total_dark & 7) {
+ err = 23;
+ goto out;
+ }
+
+ return 0;
+
+out:
+ ubifs_err(c, "bad master node at offset %d error %d", c->mst_offs, err);
+ ubifs_dump_node(c, c->mst_node);
+ return -EINVAL;
+}
+
+/**
+ * ubifs_read_master - read master node.
+ * @c: UBIFS file-system description object
+ *
+ * This function finds and reads the master node during file-system mount. If
+ * the flash is empty, it creates default master node as well. Returns zero in
+ * case of success and a negative error code in case of failure.
+ */
+int ubifs_read_master(struct ubifs_info *c)
+{
+ int err, old_leb_cnt;
+
+ c->mst_node = kzalloc(c->mst_node_alsz, GFP_KERNEL);
+ if (!c->mst_node)
+ return -ENOMEM;
+
+ err = scan_for_master(c);
+ if (err) {
+ if (err == -EUCLEAN)
+ err = ubifs_recover_master_node(c);
+ if (err)
+ /*
+ * Note, we do not free 'c->mst_node' here because the
+ * unmount routine will take care of this.
+ */
+ return err;
+ }
+
+ /* Make sure that the recovery flag is clear */
+ c->mst_node->flags &= cpu_to_le32(~UBIFS_MST_RCVRY);
+
+ c->max_sqnum = le64_to_cpu(c->mst_node->ch.sqnum);
+ c->highest_inum = le64_to_cpu(c->mst_node->highest_inum);
+ c->cmt_no = le64_to_cpu(c->mst_node->cmt_no);
+ c->zroot.lnum = le32_to_cpu(c->mst_node->root_lnum);
+ c->zroot.offs = le32_to_cpu(c->mst_node->root_offs);
+ c->zroot.len = le32_to_cpu(c->mst_node->root_len);
+ c->lhead_lnum = le32_to_cpu(c->mst_node->log_lnum);
+ c->gc_lnum = le32_to_cpu(c->mst_node->gc_lnum);
+ c->ihead_lnum = le32_to_cpu(c->mst_node->ihead_lnum);
+ c->ihead_offs = le32_to_cpu(c->mst_node->ihead_offs);
+ c->bi.old_idx_sz = le64_to_cpu(c->mst_node->index_size);
+ c->lpt_lnum = le32_to_cpu(c->mst_node->lpt_lnum);
+ c->lpt_offs = le32_to_cpu(c->mst_node->lpt_offs);
+ c->nhead_lnum = le32_to_cpu(c->mst_node->nhead_lnum);
+ c->nhead_offs = le32_to_cpu(c->mst_node->nhead_offs);
+ c->ltab_lnum = le32_to_cpu(c->mst_node->ltab_lnum);
+ c->ltab_offs = le32_to_cpu(c->mst_node->ltab_offs);
+ c->lsave_lnum = le32_to_cpu(c->mst_node->lsave_lnum);
+ c->lsave_offs = le32_to_cpu(c->mst_node->lsave_offs);
+ c->lscan_lnum = le32_to_cpu(c->mst_node->lscan_lnum);
+ c->lst.empty_lebs = le32_to_cpu(c->mst_node->empty_lebs);
+ c->lst.idx_lebs = le32_to_cpu(c->mst_node->idx_lebs);
+ old_leb_cnt = le32_to_cpu(c->mst_node->leb_cnt);
+ c->lst.total_free = le64_to_cpu(c->mst_node->total_free);
+ c->lst.total_dirty = le64_to_cpu(c->mst_node->total_dirty);
+ c->lst.total_used = le64_to_cpu(c->mst_node->total_used);
+ c->lst.total_dead = le64_to_cpu(c->mst_node->total_dead);
+ c->lst.total_dark = le64_to_cpu(c->mst_node->total_dark);
+
+ c->calc_idx_sz = c->bi.old_idx_sz;
+
+ if (c->mst_node->flags & cpu_to_le32(UBIFS_MST_NO_ORPHS))
+ c->no_orphs = 1;
+
+ if (old_leb_cnt != c->leb_cnt) {
+ /* The file system has been resized */
+ int growth = c->leb_cnt - old_leb_cnt;
+
+ if (c->leb_cnt < old_leb_cnt ||
+ c->leb_cnt < UBIFS_MIN_LEB_CNT) {
+ ubifs_err(c, "bad leb_cnt on master node");
+ ubifs_dump_node(c, c->mst_node);
+ return -EINVAL;
+ }
+
+ dbg_mnt("Auto resizing (master) from %d LEBs to %d LEBs",
+ old_leb_cnt, c->leb_cnt);
+ c->lst.empty_lebs += growth;
+ c->lst.total_free += growth * (long long)c->leb_size;
+ c->lst.total_dark += growth * (long long)c->dark_wm;
+
+ /*
+ * Reflect changes back onto the master node. N.B. the master
+ * node gets written immediately whenever mounting (or
+ * remounting) in read-write mode, so we do not need to write it
+ * here.
+ */
+ c->mst_node->leb_cnt = cpu_to_le32(c->leb_cnt);
+ c->mst_node->empty_lebs = cpu_to_le32(c->lst.empty_lebs);
+ c->mst_node->total_free = cpu_to_le64(c->lst.total_free);
+ c->mst_node->total_dark = cpu_to_le64(c->lst.total_dark);
+ }
+
+ err = validate_master(c);
+ if (err)
+ return err;
+
+#ifndef __UBOOT__
+ err = dbg_old_index_check_init(c, &c->zroot);
+#endif
+
+ return err;
+}
+
+#ifndef __UBOOT__
+/**
+ * ubifs_write_master - write master node.
+ * @c: UBIFS file-system description object
+ *
+ * This function writes the master node. Returns zero in case of success and a
+ * negative error code in case of failure. The master node is written twice to
+ * enable recovery.
+ */
+int ubifs_write_master(struct ubifs_info *c)
+{
+ int err, lnum, offs, len;
+
+ ubifs_assert(!c->ro_media && !c->ro_mount);
+ if (c->ro_error)
+ return -EROFS;
+
+ lnum = UBIFS_MST_LNUM;
+ offs = c->mst_offs + c->mst_node_alsz;
+ len = UBIFS_MST_NODE_SZ;
+
+ if (offs + UBIFS_MST_NODE_SZ > c->leb_size) {
+ err = ubifs_leb_unmap(c, lnum);
+ if (err)
+ return err;
+ offs = 0;
+ }
+
+ c->mst_offs = offs;
+ c->mst_node->highest_inum = cpu_to_le64(c->highest_inum);
+
+ err = ubifs_write_node(c, c->mst_node, len, lnum, offs);
+ if (err)
+ return err;
+
+ lnum += 1;
+
+ if (offs == 0) {
+ err = ubifs_leb_unmap(c, lnum);
+ if (err)
+ return err;
+ }
+ err = ubifs_write_node(c, c->mst_node, len, lnum, offs);
+
+ return err;
+}
+#endif
diff --git a/roms/u-boot/fs/ubifs/misc.h b/roms/u-boot/fs/ubifs/misc.h
new file mode 100644
index 000000000..449bcfccb
--- /dev/null
+++ b/roms/u-boot/fs/ubifs/misc.h
@@ -0,0 +1,300 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation
+ *
+ * Authors: Artem Bityutskiy (Битюцкий Артём)
+ * Adrian Hunter
+ */
+
+/*
+ * This file contains miscellaneous helper functions.
+ */
+
+#ifndef __UBIFS_MISC_H__
+#define __UBIFS_MISC_H__
+
+/**
+ * ubifs_zn_dirty - check if znode is dirty.
+ * @znode: znode to check
+ *
+ * This helper function returns %1 if @znode is dirty and %0 otherwise.
+ */
+static inline int ubifs_zn_dirty(const struct ubifs_znode *znode)
+{
+ return !!test_bit(DIRTY_ZNODE, &znode->flags);
+}
+
+/**
+ * ubifs_zn_obsolete - check if znode is obsolete.
+ * @znode: znode to check
+ *
+ * This helper function returns %1 if @znode is obsolete and %0 otherwise.
+ */
+static inline int ubifs_zn_obsolete(const struct ubifs_znode *znode)
+{
+ return !!test_bit(OBSOLETE_ZNODE, &znode->flags);
+}
+
+/**
+ * ubifs_zn_cow - check if znode has to be copied on write.
+ * @znode: znode to check
+ *
+ * This helper function returns %1 if @znode is has COW flag set and %0
+ * otherwise.
+ */
+static inline int ubifs_zn_cow(const struct ubifs_znode *znode)
+{
+ return !!test_bit(COW_ZNODE, &znode->flags);
+}
+
+/**
+ * ubifs_wake_up_bgt - wake up background thread.
+ * @c: UBIFS file-system description object
+ */
+static inline void ubifs_wake_up_bgt(struct ubifs_info *c)
+{
+ if (c->bgt && !c->need_bgt) {
+ c->need_bgt = 1;
+ wake_up_process(c->bgt);
+ }
+}
+
+/**
+ * ubifs_tnc_find_child - find next child in znode.
+ * @znode: znode to search at
+ * @start: the zbranch index to start at
+ *
+ * This helper function looks for znode child starting at index @start. Returns
+ * the child or %NULL if no children were found.
+ */
+static inline struct ubifs_znode *
+ubifs_tnc_find_child(struct ubifs_znode *znode, int start)
+{
+ while (start < znode->child_cnt) {
+ if (znode->zbranch[start].znode)
+ return znode->zbranch[start].znode;
+ start += 1;
+ }
+
+ return NULL;
+}
+
+/**
+ * ubifs_inode - get UBIFS inode information by VFS 'struct inode' object.
+ * @inode: the VFS 'struct inode' pointer
+ */
+static inline struct ubifs_inode *ubifs_inode(const struct inode *inode)
+{
+ return container_of(inode, struct ubifs_inode, vfs_inode);
+}
+
+/**
+ * ubifs_compr_present - check if compressor was compiled in.
+ * @compr_type: compressor type to check
+ *
+ * This function returns %1 of compressor of type @compr_type is present, and
+ * %0 if not.
+ */
+static inline int ubifs_compr_present(int compr_type)
+{
+ ubifs_assert(compr_type >= 0 && compr_type < UBIFS_COMPR_TYPES_CNT);
+ return !!ubifs_compressors[compr_type]->capi_name;
+}
+
+/**
+ * ubifs_compr_name - get compressor name string by its type.
+ * @compr_type: compressor type
+ *
+ * This function returns compressor type string.
+ */
+static inline const char *ubifs_compr_name(int compr_type)
+{
+ ubifs_assert(compr_type >= 0 && compr_type < UBIFS_COMPR_TYPES_CNT);
+ return ubifs_compressors[compr_type]->name;
+}
+
+/**
+ * ubifs_wbuf_sync - synchronize write-buffer.
+ * @wbuf: write-buffer to synchronize
+ *
+ * This is the same as as 'ubifs_wbuf_sync_nolock()' but it does not assume
+ * that the write-buffer is already locked.
+ */
+static inline int ubifs_wbuf_sync(struct ubifs_wbuf *wbuf)
+{
+ int err;
+
+ mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead);
+ err = ubifs_wbuf_sync_nolock(wbuf);
+ mutex_unlock(&wbuf->io_mutex);
+ return err;
+}
+
+#ifndef __UBOOT__
+/**
+ * ubifs_encode_dev - encode device node IDs.
+ * @dev: UBIFS device node information
+ * @rdev: device IDs to encode
+ *
+ * This is a helper function which encodes major/minor numbers of a device node
+ * into UBIFS device node description. We use standard Linux "new" and "huge"
+ * encodings.
+ */
+static inline int ubifs_encode_dev(union ubifs_dev_desc *dev, dev_t rdev)
+{
+ if (new_valid_dev(rdev)) {
+ dev->new = cpu_to_le32(new_encode_dev(rdev));
+ return sizeof(dev->new);
+ } else {
+ dev->huge = cpu_to_le64(huge_encode_dev(rdev));
+ return sizeof(dev->huge);
+ }
+}
+#endif
+
+/**
+ * ubifs_add_dirt - add dirty space to LEB properties.
+ * @c: the UBIFS file-system description object
+ * @lnum: LEB to add dirty space for
+ * @dirty: dirty space to add
+ *
+ * This is a helper function which increased amount of dirty LEB space. Returns
+ * zero in case of success and a negative error code in case of failure.
+ */
+static inline int ubifs_add_dirt(struct ubifs_info *c, int lnum, int dirty)
+{
+ return ubifs_update_one_lp(c, lnum, LPROPS_NC, dirty, 0, 0);
+}
+
+/**
+ * ubifs_return_leb - return LEB to lprops.
+ * @c: the UBIFS file-system description object
+ * @lnum: LEB to return
+ *
+ * This helper function cleans the "taken" flag of a logical eraseblock in the
+ * lprops. Returns zero in case of success and a negative error code in case of
+ * failure.
+ */
+static inline int ubifs_return_leb(struct ubifs_info *c, int lnum)
+{
+ return ubifs_change_one_lp(c, lnum, LPROPS_NC, LPROPS_NC, 0,
+ LPROPS_TAKEN, 0);
+}
+
+/**
+ * ubifs_idx_node_sz - return index node size.
+ * @c: the UBIFS file-system description object
+ * @child_cnt: number of children of this index node
+ */
+static inline int ubifs_idx_node_sz(const struct ubifs_info *c, int child_cnt)
+{
+ return UBIFS_IDX_NODE_SZ + (UBIFS_BRANCH_SZ + c->key_len) * child_cnt;
+}
+
+/**
+ * ubifs_idx_branch - return pointer to an index branch.
+ * @c: the UBIFS file-system description object
+ * @idx: index node
+ * @bnum: branch number
+ */
+static inline
+struct ubifs_branch *ubifs_idx_branch(const struct ubifs_info *c,
+ const struct ubifs_idx_node *idx,
+ int bnum)
+{
+ return (struct ubifs_branch *)((void *)idx->branches +
+ (UBIFS_BRANCH_SZ + c->key_len) * bnum);
+}
+
+/**
+ * ubifs_idx_key - return pointer to an index key.
+ * @c: the UBIFS file-system description object
+ * @idx: index node
+ */
+static inline void *ubifs_idx_key(const struct ubifs_info *c,
+ const struct ubifs_idx_node *idx)
+{
+#ifndef __UBOOT__
+ return (void *)((struct ubifs_branch *)idx->branches)->key;
+#else
+ struct ubifs_branch *tmp;
+
+ tmp = (struct ubifs_branch *)idx->branches;
+ return (void *)tmp->key;
+#endif
+}
+
+/**
+ * ubifs_current_time - round current time to time granularity.
+ * @inode: inode
+ */
+static inline struct timespec ubifs_current_time(struct inode *inode)
+{
+ return (inode->i_sb->s_time_gran < NSEC_PER_SEC) ?
+ current_fs_time(inode->i_sb) : CURRENT_TIME_SEC;
+}
+
+/**
+ * ubifs_tnc_lookup - look up a file-system node.
+ * @c: UBIFS file-system description object
+ * @key: node key to lookup
+ * @node: the node is returned here
+ *
+ * This function look up and reads node with key @key. The caller has to make
+ * sure the @node buffer is large enough to fit the node. Returns zero in case
+ * of success, %-ENOENT if the node was not found, and a negative error code in
+ * case of failure.
+ */
+static inline int ubifs_tnc_lookup(struct ubifs_info *c,
+ const union ubifs_key *key, void *node)
+{
+ return ubifs_tnc_locate(c, key, node, NULL, NULL);
+}
+
+/**
+ * ubifs_get_lprops - get reference to LEB properties.
+ * @c: the UBIFS file-system description object
+ *
+ * This function locks lprops. Lprops have to be unlocked by
+ * 'ubifs_release_lprops()'.
+ */
+static inline void ubifs_get_lprops(struct ubifs_info *c)
+{
+ mutex_lock(&c->lp_mutex);
+}
+
+/**
+ * ubifs_release_lprops - release lprops lock.
+ * @c: the UBIFS file-system description object
+ *
+ * This function has to be called after each 'ubifs_get_lprops()' call to
+ * unlock lprops.
+ */
+static inline void ubifs_release_lprops(struct ubifs_info *c)
+{
+ ubifs_assert(mutex_is_locked(&c->lp_mutex));
+ ubifs_assert(c->lst.empty_lebs >= 0 &&
+ c->lst.empty_lebs <= c->main_lebs);
+ mutex_unlock(&c->lp_mutex);
+}
+
+/**
+ * ubifs_next_log_lnum - switch to the next log LEB.
+ * @c: UBIFS file-system description object
+ * @lnum: current log LEB
+ *
+ * This helper function returns the log LEB number which goes next after LEB
+ * 'lnum'.
+ */
+static inline int ubifs_next_log_lnum(const struct ubifs_info *c, int lnum)
+{
+ lnum += 1;
+ if (lnum > c->log_last)
+ lnum = UBIFS_LOG_LNUM;
+
+ return lnum;
+}
+
+#endif /* __UBIFS_MISC_H__ */
diff --git a/roms/u-boot/fs/ubifs/orphan.c b/roms/u-boot/fs/ubifs/orphan.c
new file mode 100644
index 000000000..6aa2890ff
--- /dev/null
+++ b/roms/u-boot/fs/ubifs/orphan.c
@@ -0,0 +1,947 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ *
+ * Author: Adrian Hunter
+ */
+
+#include <log.h>
+#include <dm/devres.h>
+#include <linux/err.h>
+#include "ubifs.h"
+
+/*
+ * An orphan is an inode number whose inode node has been committed to the index
+ * with a link count of zero. That happens when an open file is deleted
+ * (unlinked) and then a commit is run. In the normal course of events the inode
+ * would be deleted when the file is closed. However in the case of an unclean
+ * unmount, orphans need to be accounted for. After an unclean unmount, the
+ * orphans' inodes must be deleted which means either scanning the entire index
+ * looking for them, or keeping a list on flash somewhere. This unit implements
+ * the latter approach.
+ *
+ * The orphan area is a fixed number of LEBs situated between the LPT area and
+ * the main area. The number of orphan area LEBs is specified when the file
+ * system is created. The minimum number is 1. The size of the orphan area
+ * should be so that it can hold the maximum number of orphans that are expected
+ * to ever exist at one time.
+ *
+ * The number of orphans that can fit in a LEB is:
+ *
+ * (c->leb_size - UBIFS_ORPH_NODE_SZ) / sizeof(__le64)
+ *
+ * For example: a 15872 byte LEB can fit 1980 orphans so 1 LEB may be enough.
+ *
+ * Orphans are accumulated in a rb-tree. When an inode's link count drops to
+ * zero, the inode number is added to the rb-tree. It is removed from the tree
+ * when the inode is deleted. Any new orphans that are in the orphan tree when
+ * the commit is run, are written to the orphan area in 1 or more orphan nodes.
+ * If the orphan area is full, it is consolidated to make space. There is
+ * always enough space because validation prevents the user from creating more
+ * than the maximum number of orphans allowed.
+ */
+
+static int dbg_check_orphans(struct ubifs_info *c);
+
+/**
+ * ubifs_add_orphan - add an orphan.
+ * @c: UBIFS file-system description object
+ * @inum: orphan inode number
+ *
+ * Add an orphan. This function is called when an inodes link count drops to
+ * zero.
+ */
+int ubifs_add_orphan(struct ubifs_info *c, ino_t inum)
+{
+ struct ubifs_orphan *orphan, *o;
+ struct rb_node **p, *parent = NULL;
+
+ orphan = kzalloc(sizeof(struct ubifs_orphan), GFP_NOFS);
+ if (!orphan)
+ return -ENOMEM;
+ orphan->inum = inum;
+ orphan->new = 1;
+
+ spin_lock(&c->orphan_lock);
+ if (c->tot_orphans >= c->max_orphans) {
+ spin_unlock(&c->orphan_lock);
+ kfree(orphan);
+ return -ENFILE;
+ }
+ p = &c->orph_tree.rb_node;
+ while (*p) {
+ parent = *p;
+ o = rb_entry(parent, struct ubifs_orphan, rb);
+ if (inum < o->inum)
+ p = &(*p)->rb_left;
+ else if (inum > o->inum)
+ p = &(*p)->rb_right;
+ else {
+ ubifs_err(c, "orphaned twice");
+ spin_unlock(&c->orphan_lock);
+ kfree(orphan);
+ return 0;
+ }
+ }
+ c->tot_orphans += 1;
+ c->new_orphans += 1;
+ rb_link_node(&orphan->rb, parent, p);
+ rb_insert_color(&orphan->rb, &c->orph_tree);
+ list_add_tail(&orphan->list, &c->orph_list);
+ list_add_tail(&orphan->new_list, &c->orph_new);
+ spin_unlock(&c->orphan_lock);
+ dbg_gen("ino %lu", (unsigned long)inum);
+ return 0;
+}
+
+/**
+ * ubifs_delete_orphan - delete an orphan.
+ * @c: UBIFS file-system description object
+ * @inum: orphan inode number
+ *
+ * Delete an orphan. This function is called when an inode is deleted.
+ */
+void ubifs_delete_orphan(struct ubifs_info *c, ino_t inum)
+{
+ struct ubifs_orphan *o;
+ struct rb_node *p;
+
+ spin_lock(&c->orphan_lock);
+ p = c->orph_tree.rb_node;
+ while (p) {
+ o = rb_entry(p, struct ubifs_orphan, rb);
+ if (inum < o->inum)
+ p = p->rb_left;
+ else if (inum > o->inum)
+ p = p->rb_right;
+ else {
+ if (o->del) {
+ spin_unlock(&c->orphan_lock);
+ dbg_gen("deleted twice ino %lu",
+ (unsigned long)inum);
+ return;
+ }
+ if (o->cmt) {
+ o->del = 1;
+ o->dnext = c->orph_dnext;
+ c->orph_dnext = o;
+ spin_unlock(&c->orphan_lock);
+ dbg_gen("delete later ino %lu",
+ (unsigned long)inum);
+ return;
+ }
+ rb_erase(p, &c->orph_tree);
+ list_del(&o->list);
+ c->tot_orphans -= 1;
+ if (o->new) {
+ list_del(&o->new_list);
+ c->new_orphans -= 1;
+ }
+ spin_unlock(&c->orphan_lock);
+ kfree(o);
+ dbg_gen("inum %lu", (unsigned long)inum);
+ return;
+ }
+ }
+ spin_unlock(&c->orphan_lock);
+ ubifs_err(c, "missing orphan ino %lu", (unsigned long)inum);
+ dump_stack();
+}
+
+/**
+ * ubifs_orphan_start_commit - start commit of orphans.
+ * @c: UBIFS file-system description object
+ *
+ * Start commit of orphans.
+ */
+int ubifs_orphan_start_commit(struct ubifs_info *c)
+{
+ struct ubifs_orphan *orphan, **last;
+
+ spin_lock(&c->orphan_lock);
+ last = &c->orph_cnext;
+ list_for_each_entry(orphan, &c->orph_new, new_list) {
+ ubifs_assert(orphan->new);
+ ubifs_assert(!orphan->cmt);
+ orphan->new = 0;
+ orphan->cmt = 1;
+ *last = orphan;
+ last = &orphan->cnext;
+ }
+ *last = NULL;
+ c->cmt_orphans = c->new_orphans;
+ c->new_orphans = 0;
+ dbg_cmt("%d orphans to commit", c->cmt_orphans);
+ INIT_LIST_HEAD(&c->orph_new);
+ if (c->tot_orphans == 0)
+ c->no_orphs = 1;
+ else
+ c->no_orphs = 0;
+ spin_unlock(&c->orphan_lock);
+ return 0;
+}
+
+/**
+ * avail_orphs - calculate available space.
+ * @c: UBIFS file-system description object
+ *
+ * This function returns the number of orphans that can be written in the
+ * available space.
+ */
+static int avail_orphs(struct ubifs_info *c)
+{
+ int avail_lebs, avail, gap;
+
+ avail_lebs = c->orph_lebs - (c->ohead_lnum - c->orph_first) - 1;
+ avail = avail_lebs *
+ ((c->leb_size - UBIFS_ORPH_NODE_SZ) / sizeof(__le64));
+ gap = c->leb_size - c->ohead_offs;
+ if (gap >= UBIFS_ORPH_NODE_SZ + sizeof(__le64))
+ avail += (gap - UBIFS_ORPH_NODE_SZ) / sizeof(__le64);
+ return avail;
+}
+
+/**
+ * tot_avail_orphs - calculate total space.
+ * @c: UBIFS file-system description object
+ *
+ * This function returns the number of orphans that can be written in half
+ * the total space. That leaves half the space for adding new orphans.
+ */
+static int tot_avail_orphs(struct ubifs_info *c)
+{
+ int avail_lebs, avail;
+
+ avail_lebs = c->orph_lebs;
+ avail = avail_lebs *
+ ((c->leb_size - UBIFS_ORPH_NODE_SZ) / sizeof(__le64));
+ return avail / 2;
+}
+
+/**
+ * do_write_orph_node - write a node to the orphan head.
+ * @c: UBIFS file-system description object
+ * @len: length of node
+ * @atomic: write atomically
+ *
+ * This function writes a node to the orphan head from the orphan buffer. If
+ * %atomic is not zero, then the write is done atomically. On success, %0 is
+ * returned, otherwise a negative error code is returned.
+ */
+static int do_write_orph_node(struct ubifs_info *c, int len, int atomic)
+{
+ int err = 0;
+
+ if (atomic) {
+ ubifs_assert(c->ohead_offs == 0);
+ ubifs_prepare_node(c, c->orph_buf, len, 1);
+ len = ALIGN(len, c->min_io_size);
+ err = ubifs_leb_change(c, c->ohead_lnum, c->orph_buf, len);
+ } else {
+ if (c->ohead_offs == 0) {
+ /* Ensure LEB has been unmapped */
+ err = ubifs_leb_unmap(c, c->ohead_lnum);
+ if (err)
+ return err;
+ }
+ err = ubifs_write_node(c, c->orph_buf, len, c->ohead_lnum,
+ c->ohead_offs);
+ }
+ return err;
+}
+
+/**
+ * write_orph_node - write an orphan node.
+ * @c: UBIFS file-system description object
+ * @atomic: write atomically
+ *
+ * This function builds an orphan node from the cnext list and writes it to the
+ * orphan head. On success, %0 is returned, otherwise a negative error code
+ * is returned.
+ */
+static int write_orph_node(struct ubifs_info *c, int atomic)
+{
+ struct ubifs_orphan *orphan, *cnext;
+ struct ubifs_orph_node *orph;
+ int gap, err, len, cnt, i;
+
+ ubifs_assert(c->cmt_orphans > 0);
+ gap = c->leb_size - c->ohead_offs;
+ if (gap < UBIFS_ORPH_NODE_SZ + sizeof(__le64)) {
+ c->ohead_lnum += 1;
+ c->ohead_offs = 0;
+ gap = c->leb_size;
+ if (c->ohead_lnum > c->orph_last) {
+ /*
+ * We limit the number of orphans so that this should
+ * never happen.
+ */
+ ubifs_err(c, "out of space in orphan area");
+ return -EINVAL;
+ }
+ }
+ cnt = (gap - UBIFS_ORPH_NODE_SZ) / sizeof(__le64);
+ if (cnt > c->cmt_orphans)
+ cnt = c->cmt_orphans;
+ len = UBIFS_ORPH_NODE_SZ + cnt * sizeof(__le64);
+ ubifs_assert(c->orph_buf);
+ orph = c->orph_buf;
+ orph->ch.node_type = UBIFS_ORPH_NODE;
+ spin_lock(&c->orphan_lock);
+ cnext = c->orph_cnext;
+ for (i = 0; i < cnt; i++) {
+ orphan = cnext;
+ ubifs_assert(orphan->cmt);
+ orph->inos[i] = cpu_to_le64(orphan->inum);
+ orphan->cmt = 0;
+ cnext = orphan->cnext;
+ orphan->cnext = NULL;
+ }
+ c->orph_cnext = cnext;
+ c->cmt_orphans -= cnt;
+ spin_unlock(&c->orphan_lock);
+ if (c->cmt_orphans)
+ orph->cmt_no = cpu_to_le64(c->cmt_no);
+ else
+ /* Mark the last node of the commit */
+ orph->cmt_no = cpu_to_le64((c->cmt_no) | (1ULL << 63));
+ ubifs_assert(c->ohead_offs + len <= c->leb_size);
+ ubifs_assert(c->ohead_lnum >= c->orph_first);
+ ubifs_assert(c->ohead_lnum <= c->orph_last);
+ err = do_write_orph_node(c, len, atomic);
+ c->ohead_offs += ALIGN(len, c->min_io_size);
+ c->ohead_offs = ALIGN(c->ohead_offs, 8);
+ return err;
+}
+
+/**
+ * write_orph_nodes - write orphan nodes until there are no more to commit.
+ * @c: UBIFS file-system description object
+ * @atomic: write atomically
+ *
+ * This function writes orphan nodes for all the orphans to commit. On success,
+ * %0 is returned, otherwise a negative error code is returned.
+ */
+static int write_orph_nodes(struct ubifs_info *c, int atomic)
+{
+ int err;
+
+ while (c->cmt_orphans > 0) {
+ err = write_orph_node(c, atomic);
+ if (err)
+ return err;
+ }
+ if (atomic) {
+ int lnum;
+
+ /* Unmap any unused LEBs after consolidation */
+ for (lnum = c->ohead_lnum + 1; lnum <= c->orph_last; lnum++) {
+ err = ubifs_leb_unmap(c, lnum);
+ if (err)
+ return err;
+ }
+ }
+ return 0;
+}
+
+/**
+ * consolidate - consolidate the orphan area.
+ * @c: UBIFS file-system description object
+ *
+ * This function enables consolidation by putting all the orphans into the list
+ * to commit. The list is in the order that the orphans were added, and the
+ * LEBs are written atomically in order, so at no time can orphans be lost by
+ * an unclean unmount.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int consolidate(struct ubifs_info *c)
+{
+ int tot_avail = tot_avail_orphs(c), err = 0;
+
+ spin_lock(&c->orphan_lock);
+ dbg_cmt("there is space for %d orphans and there are %d",
+ tot_avail, c->tot_orphans);
+ if (c->tot_orphans - c->new_orphans <= tot_avail) {
+ struct ubifs_orphan *orphan, **last;
+ int cnt = 0;
+
+ /* Change the cnext list to include all non-new orphans */
+ last = &c->orph_cnext;
+ list_for_each_entry(orphan, &c->orph_list, list) {
+ if (orphan->new)
+ continue;
+ orphan->cmt = 1;
+ *last = orphan;
+ last = &orphan->cnext;
+ cnt += 1;
+ }
+ *last = NULL;
+ ubifs_assert(cnt == c->tot_orphans - c->new_orphans);
+ c->cmt_orphans = cnt;
+ c->ohead_lnum = c->orph_first;
+ c->ohead_offs = 0;
+ } else {
+ /*
+ * We limit the number of orphans so that this should
+ * never happen.
+ */
+ ubifs_err(c, "out of space in orphan area");
+ err = -EINVAL;
+ }
+ spin_unlock(&c->orphan_lock);
+ return err;
+}
+
+/**
+ * commit_orphans - commit orphans.
+ * @c: UBIFS file-system description object
+ *
+ * This function commits orphans to flash. On success, %0 is returned,
+ * otherwise a negative error code is returned.
+ */
+static int commit_orphans(struct ubifs_info *c)
+{
+ int avail, atomic = 0, err;
+
+ ubifs_assert(c->cmt_orphans > 0);
+ avail = avail_orphs(c);
+ if (avail < c->cmt_orphans) {
+ /* Not enough space to write new orphans, so consolidate */
+ err = consolidate(c);
+ if (err)
+ return err;
+ atomic = 1;
+ }
+ err = write_orph_nodes(c, atomic);
+ return err;
+}
+
+/**
+ * erase_deleted - erase the orphans marked for deletion.
+ * @c: UBIFS file-system description object
+ *
+ * During commit, the orphans being committed cannot be deleted, so they are
+ * marked for deletion and deleted by this function. Also, the recovery
+ * adds killed orphans to the deletion list, and therefore they are deleted
+ * here too.
+ */
+static void erase_deleted(struct ubifs_info *c)
+{
+ struct ubifs_orphan *orphan, *dnext;
+
+ spin_lock(&c->orphan_lock);
+ dnext = c->orph_dnext;
+ while (dnext) {
+ orphan = dnext;
+ dnext = orphan->dnext;
+ ubifs_assert(!orphan->new);
+ ubifs_assert(orphan->del);
+ rb_erase(&orphan->rb, &c->orph_tree);
+ list_del(&orphan->list);
+ c->tot_orphans -= 1;
+ dbg_gen("deleting orphan ino %lu", (unsigned long)orphan->inum);
+ kfree(orphan);
+ }
+ c->orph_dnext = NULL;
+ spin_unlock(&c->orphan_lock);
+}
+
+/**
+ * ubifs_orphan_end_commit - end commit of orphans.
+ * @c: UBIFS file-system description object
+ *
+ * End commit of orphans.
+ */
+int ubifs_orphan_end_commit(struct ubifs_info *c)
+{
+ int err;
+
+ if (c->cmt_orphans != 0) {
+ err = commit_orphans(c);
+ if (err)
+ return err;
+ }
+ erase_deleted(c);
+ err = dbg_check_orphans(c);
+ return err;
+}
+
+/**
+ * ubifs_clear_orphans - erase all LEBs used for orphans.
+ * @c: UBIFS file-system description object
+ *
+ * If recovery is not required, then the orphans from the previous session
+ * are not needed. This function locates the LEBs used to record
+ * orphans, and un-maps them.
+ */
+int ubifs_clear_orphans(struct ubifs_info *c)
+{
+ int lnum, err;
+
+ for (lnum = c->orph_first; lnum <= c->orph_last; lnum++) {
+ err = ubifs_leb_unmap(c, lnum);
+ if (err)
+ return err;
+ }
+ c->ohead_lnum = c->orph_first;
+ c->ohead_offs = 0;
+ return 0;
+}
+
+/**
+ * insert_dead_orphan - insert an orphan.
+ * @c: UBIFS file-system description object
+ * @inum: orphan inode number
+ *
+ * This function is a helper to the 'do_kill_orphans()' function. The orphan
+ * must be kept until the next commit, so it is added to the rb-tree and the
+ * deletion list.
+ */
+static int insert_dead_orphan(struct ubifs_info *c, ino_t inum)
+{
+ struct ubifs_orphan *orphan, *o;
+ struct rb_node **p, *parent = NULL;
+
+ orphan = kzalloc(sizeof(struct ubifs_orphan), GFP_KERNEL);
+ if (!orphan)
+ return -ENOMEM;
+ orphan->inum = inum;
+
+ p = &c->orph_tree.rb_node;
+ while (*p) {
+ parent = *p;
+ o = rb_entry(parent, struct ubifs_orphan, rb);
+ if (inum < o->inum)
+ p = &(*p)->rb_left;
+ else if (inum > o->inum)
+ p = &(*p)->rb_right;
+ else {
+ /* Already added - no problem */
+ kfree(orphan);
+ return 0;
+ }
+ }
+ c->tot_orphans += 1;
+ rb_link_node(&orphan->rb, parent, p);
+ rb_insert_color(&orphan->rb, &c->orph_tree);
+ list_add_tail(&orphan->list, &c->orph_list);
+ orphan->del = 1;
+ orphan->dnext = c->orph_dnext;
+ c->orph_dnext = orphan;
+ dbg_mnt("ino %lu, new %d, tot %d", (unsigned long)inum,
+ c->new_orphans, c->tot_orphans);
+ return 0;
+}
+
+/**
+ * do_kill_orphans - remove orphan inodes from the index.
+ * @c: UBIFS file-system description object
+ * @sleb: scanned LEB
+ * @last_cmt_no: cmt_no of last orphan node read is passed and returned here
+ * @outofdate: whether the LEB is out of date is returned here
+ * @last_flagged: whether the end orphan node is encountered
+ *
+ * This function is a helper to the 'kill_orphans()' function. It goes through
+ * every orphan node in a LEB and for every inode number recorded, removes
+ * all keys for that inode from the TNC.
+ */
+static int do_kill_orphans(struct ubifs_info *c, struct ubifs_scan_leb *sleb,
+ unsigned long long *last_cmt_no, int *outofdate,
+ int *last_flagged)
+{
+ struct ubifs_scan_node *snod;
+ struct ubifs_orph_node *orph;
+ unsigned long long cmt_no;
+ ino_t inum;
+ int i, n, err, first = 1;
+
+ list_for_each_entry(snod, &sleb->nodes, list) {
+ if (snod->type != UBIFS_ORPH_NODE) {
+ ubifs_err(c, "invalid node type %d in orphan area at %d:%d",
+ snod->type, sleb->lnum, snod->offs);
+ ubifs_dump_node(c, snod->node);
+ return -EINVAL;
+ }
+
+ orph = snod->node;
+
+ /* Check commit number */
+ cmt_no = le64_to_cpu(orph->cmt_no) & LLONG_MAX;
+ /*
+ * The commit number on the master node may be less, because
+ * of a failed commit. If there are several failed commits in a
+ * row, the commit number written on orphan nodes will continue
+ * to increase (because the commit number is adjusted here) even
+ * though the commit number on the master node stays the same
+ * because the master node has not been re-written.
+ */
+ if (cmt_no > c->cmt_no)
+ c->cmt_no = cmt_no;
+ if (cmt_no < *last_cmt_no && *last_flagged) {
+ /*
+ * The last orphan node had a higher commit number and
+ * was flagged as the last written for that commit
+ * number. That makes this orphan node, out of date.
+ */
+ if (!first) {
+ ubifs_err(c, "out of order commit number %llu in orphan node at %d:%d",
+ cmt_no, sleb->lnum, snod->offs);
+ ubifs_dump_node(c, snod->node);
+ return -EINVAL;
+ }
+ dbg_rcvry("out of date LEB %d", sleb->lnum);
+ *outofdate = 1;
+ return 0;
+ }
+
+ if (first)
+ first = 0;
+
+ n = (le32_to_cpu(orph->ch.len) - UBIFS_ORPH_NODE_SZ) >> 3;
+ for (i = 0; i < n; i++) {
+ inum = le64_to_cpu(orph->inos[i]);
+ dbg_rcvry("deleting orphaned inode %lu",
+ (unsigned long)inum);
+ err = ubifs_tnc_remove_ino(c, inum);
+ if (err)
+ return err;
+ err = insert_dead_orphan(c, inum);
+ if (err)
+ return err;
+ }
+
+ *last_cmt_no = cmt_no;
+ if (le64_to_cpu(orph->cmt_no) & (1ULL << 63)) {
+ dbg_rcvry("last orph node for commit %llu at %d:%d",
+ cmt_no, sleb->lnum, snod->offs);
+ *last_flagged = 1;
+ } else
+ *last_flagged = 0;
+ }
+
+ return 0;
+}
+
+/**
+ * kill_orphans - remove all orphan inodes from the index.
+ * @c: UBIFS file-system description object
+ *
+ * If recovery is required, then orphan inodes recorded during the previous
+ * session (which ended with an unclean unmount) must be deleted from the index.
+ * This is done by updating the TNC, but since the index is not updated until
+ * the next commit, the LEBs where the orphan information is recorded are not
+ * erased until the next commit.
+ */
+static int kill_orphans(struct ubifs_info *c)
+{
+ unsigned long long last_cmt_no = 0;
+ int lnum, err = 0, outofdate = 0, last_flagged = 0;
+
+ c->ohead_lnum = c->orph_first;
+ c->ohead_offs = 0;
+ /* Check no-orphans flag and skip this if no orphans */
+ if (c->no_orphs) {
+ dbg_rcvry("no orphans");
+ return 0;
+ }
+ /*
+ * Orph nodes always start at c->orph_first and are written to each
+ * successive LEB in turn. Generally unused LEBs will have been unmapped
+ * but may contain out of date orphan nodes if the unmap didn't go
+ * through. In addition, the last orphan node written for each commit is
+ * marked (top bit of orph->cmt_no is set to 1). It is possible that
+ * there are orphan nodes from the next commit (i.e. the commit did not
+ * complete successfully). In that case, no orphans will have been lost
+ * due to the way that orphans are written, and any orphans added will
+ * be valid orphans anyway and so can be deleted.
+ */
+ for (lnum = c->orph_first; lnum <= c->orph_last; lnum++) {
+ struct ubifs_scan_leb *sleb;
+
+ dbg_rcvry("LEB %d", lnum);
+ sleb = ubifs_scan(c, lnum, 0, c->sbuf, 1);
+ if (IS_ERR(sleb)) {
+ if (PTR_ERR(sleb) == -EUCLEAN)
+ sleb = ubifs_recover_leb(c, lnum, 0,
+ c->sbuf, -1);
+ if (IS_ERR(sleb)) {
+ err = PTR_ERR(sleb);
+ break;
+ }
+ }
+ err = do_kill_orphans(c, sleb, &last_cmt_no, &outofdate,
+ &last_flagged);
+ if (err || outofdate) {
+ ubifs_scan_destroy(sleb);
+ break;
+ }
+ if (sleb->endpt) {
+ c->ohead_lnum = lnum;
+ c->ohead_offs = sleb->endpt;
+ }
+ ubifs_scan_destroy(sleb);
+ }
+ return err;
+}
+
+/**
+ * ubifs_mount_orphans - delete orphan inodes and erase LEBs that recorded them.
+ * @c: UBIFS file-system description object
+ * @unclean: indicates recovery from unclean unmount
+ * @read_only: indicates read only mount
+ *
+ * This function is called when mounting to erase orphans from the previous
+ * session. If UBIFS was not unmounted cleanly, then the inodes recorded as
+ * orphans are deleted.
+ */
+int ubifs_mount_orphans(struct ubifs_info *c, int unclean, int read_only)
+{
+ int err = 0;
+
+ c->max_orphans = tot_avail_orphs(c);
+
+ if (!read_only) {
+ c->orph_buf = vmalloc(c->leb_size);
+ if (!c->orph_buf)
+ return -ENOMEM;
+ }
+
+ if (unclean)
+ err = kill_orphans(c);
+ else if (!read_only)
+ err = ubifs_clear_orphans(c);
+
+ return err;
+}
+
+/*
+ * Everything below is related to debugging.
+ */
+
+struct check_orphan {
+ struct rb_node rb;
+ ino_t inum;
+};
+
+struct check_info {
+ unsigned long last_ino;
+ unsigned long tot_inos;
+ unsigned long missing;
+ unsigned long long leaf_cnt;
+ struct ubifs_ino_node *node;
+ struct rb_root root;
+};
+
+static int dbg_find_orphan(struct ubifs_info *c, ino_t inum)
+{
+ struct ubifs_orphan *o;
+ struct rb_node *p;
+
+ spin_lock(&c->orphan_lock);
+ p = c->orph_tree.rb_node;
+ while (p) {
+ o = rb_entry(p, struct ubifs_orphan, rb);
+ if (inum < o->inum)
+ p = p->rb_left;
+ else if (inum > o->inum)
+ p = p->rb_right;
+ else {
+ spin_unlock(&c->orphan_lock);
+ return 1;
+ }
+ }
+ spin_unlock(&c->orphan_lock);
+ return 0;
+}
+
+static int dbg_ins_check_orphan(struct rb_root *root, ino_t inum)
+{
+ struct check_orphan *orphan, *o;
+ struct rb_node **p, *parent = NULL;
+
+ orphan = kzalloc(sizeof(struct check_orphan), GFP_NOFS);
+ if (!orphan)
+ return -ENOMEM;
+ orphan->inum = inum;
+
+ p = &root->rb_node;
+ while (*p) {
+ parent = *p;
+ o = rb_entry(parent, struct check_orphan, rb);
+ if (inum < o->inum)
+ p = &(*p)->rb_left;
+ else if (inum > o->inum)
+ p = &(*p)->rb_right;
+ else {
+ kfree(orphan);
+ return 0;
+ }
+ }
+ rb_link_node(&orphan->rb, parent, p);
+ rb_insert_color(&orphan->rb, root);
+ return 0;
+}
+
+static int dbg_find_check_orphan(struct rb_root *root, ino_t inum)
+{
+ struct check_orphan *o;
+ struct rb_node *p;
+
+ p = root->rb_node;
+ while (p) {
+ o = rb_entry(p, struct check_orphan, rb);
+ if (inum < o->inum)
+ p = p->rb_left;
+ else if (inum > o->inum)
+ p = p->rb_right;
+ else
+ return 1;
+ }
+ return 0;
+}
+
+static void dbg_free_check_tree(struct rb_root *root)
+{
+ struct check_orphan *o, *n;
+
+ rbtree_postorder_for_each_entry_safe(o, n, root, rb)
+ kfree(o);
+}
+
+static int dbg_orphan_check(struct ubifs_info *c, struct ubifs_zbranch *zbr,
+ void *priv)
+{
+ struct check_info *ci = priv;
+ ino_t inum;
+ int err;
+
+ inum = key_inum(c, &zbr->key);
+ if (inum != ci->last_ino) {
+ /* Lowest node type is the inode node, so it comes first */
+ if (key_type(c, &zbr->key) != UBIFS_INO_KEY)
+ ubifs_err(c, "found orphan node ino %lu, type %d",
+ (unsigned long)inum, key_type(c, &zbr->key));
+ ci->last_ino = inum;
+ ci->tot_inos += 1;
+ err = ubifs_tnc_read_node(c, zbr, ci->node);
+ if (err) {
+ ubifs_err(c, "node read failed, error %d", err);
+ return err;
+ }
+ if (ci->node->nlink == 0)
+ /* Must be recorded as an orphan */
+ if (!dbg_find_check_orphan(&ci->root, inum) &&
+ !dbg_find_orphan(c, inum)) {
+ ubifs_err(c, "missing orphan, ino %lu",
+ (unsigned long)inum);
+ ci->missing += 1;
+ }
+ }
+ ci->leaf_cnt += 1;
+ return 0;
+}
+
+static int dbg_read_orphans(struct check_info *ci, struct ubifs_scan_leb *sleb)
+{
+ struct ubifs_scan_node *snod;
+ struct ubifs_orph_node *orph;
+ ino_t inum;
+ int i, n, err;
+
+ list_for_each_entry(snod, &sleb->nodes, list) {
+ cond_resched();
+ if (snod->type != UBIFS_ORPH_NODE)
+ continue;
+ orph = snod->node;
+ n = (le32_to_cpu(orph->ch.len) - UBIFS_ORPH_NODE_SZ) >> 3;
+ for (i = 0; i < n; i++) {
+ inum = le64_to_cpu(orph->inos[i]);
+ err = dbg_ins_check_orphan(&ci->root, inum);
+ if (err)
+ return err;
+ }
+ }
+ return 0;
+}
+
+static int dbg_scan_orphans(struct ubifs_info *c, struct check_info *ci)
+{
+ int lnum, err = 0;
+ void *buf;
+
+ /* Check no-orphans flag and skip this if no orphans */
+ if (c->no_orphs)
+ return 0;
+
+ buf = __vmalloc(c->leb_size, GFP_NOFS, PAGE_KERNEL);
+ if (!buf) {
+ ubifs_err(c, "cannot allocate memory to check orphans");
+ return 0;
+ }
+
+ for (lnum = c->orph_first; lnum <= c->orph_last; lnum++) {
+ struct ubifs_scan_leb *sleb;
+
+ sleb = ubifs_scan(c, lnum, 0, buf, 0);
+ if (IS_ERR(sleb)) {
+ err = PTR_ERR(sleb);
+ break;
+ }
+
+ err = dbg_read_orphans(ci, sleb);
+ ubifs_scan_destroy(sleb);
+ if (err)
+ break;
+ }
+
+ vfree(buf);
+ return err;
+}
+
+static int dbg_check_orphans(struct ubifs_info *c)
+{
+ struct check_info ci;
+ int err;
+
+ if (!dbg_is_chk_orph(c))
+ return 0;
+
+ ci.last_ino = 0;
+ ci.tot_inos = 0;
+ ci.missing = 0;
+ ci.leaf_cnt = 0;
+ ci.root = RB_ROOT;
+ ci.node = kmalloc(UBIFS_MAX_INO_NODE_SZ, GFP_NOFS);
+ if (!ci.node) {
+ ubifs_err(c, "out of memory");
+ return -ENOMEM;
+ }
+
+ err = dbg_scan_orphans(c, &ci);
+ if (err)
+ goto out;
+
+ err = dbg_walk_index(c, &dbg_orphan_check, NULL, &ci);
+ if (err) {
+ ubifs_err(c, "cannot scan TNC, error %d", err);
+ goto out;
+ }
+
+ if (ci.missing) {
+ ubifs_err(c, "%lu missing orphan(s)", ci.missing);
+ err = -EINVAL;
+ goto out;
+ }
+
+ dbg_cmt("last inode number is %lu", ci.last_ino);
+ dbg_cmt("total number of inodes is %lu", ci.tot_inos);
+ dbg_cmt("total number of leaf nodes is %llu", ci.leaf_cnt);
+
+out:
+ dbg_free_check_tree(&ci.root);
+ kfree(ci.node);
+ return err;
+}
diff --git a/roms/u-boot/fs/ubifs/recovery.c b/roms/u-boot/fs/ubifs/recovery.c
new file mode 100644
index 000000000..6b6884fb2
--- /dev/null
+++ b/roms/u-boot/fs/ubifs/recovery.c
@@ -0,0 +1,1557 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation
+ *
+ * Authors: Adrian Hunter
+ * Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * This file implements functions needed to recover from unclean un-mounts.
+ * When UBIFS is mounted, it checks a flag on the master node to determine if
+ * an un-mount was completed successfully. If not, the process of mounting
+ * incorporates additional checking and fixing of on-flash data structures.
+ * UBIFS always cleans away all remnants of an unclean un-mount, so that
+ * errors do not accumulate. However UBIFS defers recovery if it is mounted
+ * read-only, and the flash is not modified in that case.
+ *
+ * The general UBIFS approach to the recovery is that it recovers from
+ * corruptions which could be caused by power cuts, but it refuses to recover
+ * from corruption caused by other reasons. And UBIFS tries to distinguish
+ * between these 2 reasons of corruptions and silently recover in the former
+ * case and loudly complain in the latter case.
+ *
+ * UBIFS writes only to erased LEBs, so it writes only to the flash space
+ * containing only 0xFFs. UBIFS also always writes strictly from the beginning
+ * of the LEB to the end. And UBIFS assumes that the underlying flash media
+ * writes in @c->max_write_size bytes at a time.
+ *
+ * Hence, if UBIFS finds a corrupted node at offset X, it expects only the min.
+ * I/O unit corresponding to offset X to contain corrupted data, all the
+ * following min. I/O units have to contain empty space (all 0xFFs). If this is
+ * not true, the corruption cannot be the result of a power cut, and UBIFS
+ * refuses to mount.
+ */
+
+#ifndef __UBOOT__
+#include <log.h>
+#include <dm/devres.h>
+#include <linux/crc32.h>
+#include <linux/slab.h>
+#include <u-boot/crc.h>
+#else
+#include <linux/err.h>
+#endif
+#include "ubifs.h"
+
+/**
+ * is_empty - determine whether a buffer is empty (contains all 0xff).
+ * @buf: buffer to clean
+ * @len: length of buffer
+ *
+ * This function returns %1 if the buffer is empty (contains all 0xff) otherwise
+ * %0 is returned.
+ */
+static int is_empty(void *buf, int len)
+{
+ uint8_t *p = buf;
+ int i;
+
+ for (i = 0; i < len; i++)
+ if (*p++ != 0xff)
+ return 0;
+ return 1;
+}
+
+/**
+ * first_non_ff - find offset of the first non-0xff byte.
+ * @buf: buffer to search in
+ * @len: length of buffer
+ *
+ * This function returns offset of the first non-0xff byte in @buf or %-1 if
+ * the buffer contains only 0xff bytes.
+ */
+static int first_non_ff(void *buf, int len)
+{
+ uint8_t *p = buf;
+ int i;
+
+ for (i = 0; i < len; i++)
+ if (*p++ != 0xff)
+ return i;
+ return -1;
+}
+
+/**
+ * get_master_node - get the last valid master node allowing for corruption.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number
+ * @pbuf: buffer containing the LEB read, is returned here
+ * @mst: master node, if found, is returned here
+ * @cor: corruption, if found, is returned here
+ *
+ * This function allocates a buffer, reads the LEB into it, and finds and
+ * returns the last valid master node allowing for one area of corruption.
+ * The corrupt area, if there is one, must be consistent with the assumption
+ * that it is the result of an unclean unmount while the master node was being
+ * written. Under those circumstances, it is valid to use the previously written
+ * master node.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int get_master_node(const struct ubifs_info *c, int lnum, void **pbuf,
+ struct ubifs_mst_node **mst, void **cor)
+{
+ const int sz = c->mst_node_alsz;
+ int err, offs, len;
+ void *sbuf, *buf;
+
+ sbuf = vmalloc(c->leb_size);
+ if (!sbuf)
+ return -ENOMEM;
+
+ err = ubifs_leb_read(c, lnum, sbuf, 0, c->leb_size, 0);
+ if (err && err != -EBADMSG)
+ goto out_free;
+
+ /* Find the first position that is definitely not a node */
+ offs = 0;
+ buf = sbuf;
+ len = c->leb_size;
+ while (offs + UBIFS_MST_NODE_SZ <= c->leb_size) {
+ struct ubifs_ch *ch = buf;
+
+ if (le32_to_cpu(ch->magic) != UBIFS_NODE_MAGIC)
+ break;
+ offs += sz;
+ buf += sz;
+ len -= sz;
+ }
+ /* See if there was a valid master node before that */
+ if (offs) {
+ int ret;
+
+ offs -= sz;
+ buf -= sz;
+ len += sz;
+ ret = ubifs_scan_a_node(c, buf, len, lnum, offs, 1);
+ if (ret != SCANNED_A_NODE && offs) {
+ /* Could have been corruption so check one place back */
+ offs -= sz;
+ buf -= sz;
+ len += sz;
+ ret = ubifs_scan_a_node(c, buf, len, lnum, offs, 1);
+ if (ret != SCANNED_A_NODE)
+ /*
+ * We accept only one area of corruption because
+ * we are assuming that it was caused while
+ * trying to write a master node.
+ */
+ goto out_err;
+ }
+ if (ret == SCANNED_A_NODE) {
+ struct ubifs_ch *ch = buf;
+
+ if (ch->node_type != UBIFS_MST_NODE)
+ goto out_err;
+ dbg_rcvry("found a master node at %d:%d", lnum, offs);
+ *mst = buf;
+ offs += sz;
+ buf += sz;
+ len -= sz;
+ }
+ }
+ /* Check for corruption */
+ if (offs < c->leb_size) {
+ if (!is_empty(buf, min_t(int, len, sz))) {
+ *cor = buf;
+ dbg_rcvry("found corruption at %d:%d", lnum, offs);
+ }
+ offs += sz;
+ buf += sz;
+ len -= sz;
+ }
+ /* Check remaining empty space */
+ if (offs < c->leb_size)
+ if (!is_empty(buf, len))
+ goto out_err;
+ *pbuf = sbuf;
+ return 0;
+
+out_err:
+ err = -EINVAL;
+out_free:
+ vfree(sbuf);
+ *mst = NULL;
+ *cor = NULL;
+ return err;
+}
+
+/**
+ * write_rcvrd_mst_node - write recovered master node.
+ * @c: UBIFS file-system description object
+ * @mst: master node
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int write_rcvrd_mst_node(struct ubifs_info *c,
+ struct ubifs_mst_node *mst)
+{
+ int err = 0, lnum = UBIFS_MST_LNUM, sz = c->mst_node_alsz;
+ __le32 save_flags;
+
+ dbg_rcvry("recovery");
+
+ save_flags = mst->flags;
+ mst->flags |= cpu_to_le32(UBIFS_MST_RCVRY);
+
+ ubifs_prepare_node(c, mst, UBIFS_MST_NODE_SZ, 1);
+ err = ubifs_leb_change(c, lnum, mst, sz);
+ if (err)
+ goto out;
+ err = ubifs_leb_change(c, lnum + 1, mst, sz);
+ if (err)
+ goto out;
+out:
+ mst->flags = save_flags;
+ return err;
+}
+
+/**
+ * ubifs_recover_master_node - recover the master node.
+ * @c: UBIFS file-system description object
+ *
+ * This function recovers the master node from corruption that may occur due to
+ * an unclean unmount.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int ubifs_recover_master_node(struct ubifs_info *c)
+{
+ void *buf1 = NULL, *buf2 = NULL, *cor1 = NULL, *cor2 = NULL;
+ struct ubifs_mst_node *mst1 = NULL, *mst2 = NULL, *mst;
+ const int sz = c->mst_node_alsz;
+ int err, offs1, offs2;
+
+ dbg_rcvry("recovery");
+
+ err = get_master_node(c, UBIFS_MST_LNUM, &buf1, &mst1, &cor1);
+ if (err)
+ goto out_free;
+
+ err = get_master_node(c, UBIFS_MST_LNUM + 1, &buf2, &mst2, &cor2);
+ if (err)
+ goto out_free;
+
+ if (mst1) {
+ offs1 = (void *)mst1 - buf1;
+ if ((le32_to_cpu(mst1->flags) & UBIFS_MST_RCVRY) &&
+ (offs1 == 0 && !cor1)) {
+ /*
+ * mst1 was written by recovery at offset 0 with no
+ * corruption.
+ */
+ dbg_rcvry("recovery recovery");
+ mst = mst1;
+ } else if (mst2) {
+ offs2 = (void *)mst2 - buf2;
+ if (offs1 == offs2) {
+ /* Same offset, so must be the same */
+ if (memcmp((void *)mst1 + UBIFS_CH_SZ,
+ (void *)mst2 + UBIFS_CH_SZ,
+ UBIFS_MST_NODE_SZ - UBIFS_CH_SZ))
+ goto out_err;
+ mst = mst1;
+ } else if (offs2 + sz == offs1) {
+ /* 1st LEB was written, 2nd was not */
+ if (cor1)
+ goto out_err;
+ mst = mst1;
+ } else if (offs1 == 0 &&
+ c->leb_size - offs2 - sz < sz) {
+ /* 1st LEB was unmapped and written, 2nd not */
+ if (cor1)
+ goto out_err;
+ mst = mst1;
+ } else
+ goto out_err;
+ } else {
+ /*
+ * 2nd LEB was unmapped and about to be written, so
+ * there must be only one master node in the first LEB
+ * and no corruption.
+ */
+ if (offs1 != 0 || cor1)
+ goto out_err;
+ mst = mst1;
+ }
+ } else {
+ if (!mst2)
+ goto out_err;
+ /*
+ * 1st LEB was unmapped and about to be written, so there must
+ * be no room left in 2nd LEB.
+ */
+ offs2 = (void *)mst2 - buf2;
+ if (offs2 + sz + sz <= c->leb_size)
+ goto out_err;
+ mst = mst2;
+ }
+
+ ubifs_msg(c, "recovered master node from LEB %d",
+ (mst == mst1 ? UBIFS_MST_LNUM : UBIFS_MST_LNUM + 1));
+
+ memcpy(c->mst_node, mst, UBIFS_MST_NODE_SZ);
+
+ if (c->ro_mount) {
+ /* Read-only mode. Keep a copy for switching to rw mode */
+ c->rcvrd_mst_node = kmalloc(sz, GFP_KERNEL);
+ if (!c->rcvrd_mst_node) {
+ err = -ENOMEM;
+ goto out_free;
+ }
+ memcpy(c->rcvrd_mst_node, c->mst_node, UBIFS_MST_NODE_SZ);
+
+ /*
+ * We had to recover the master node, which means there was an
+ * unclean reboot. However, it is possible that the master node
+ * is clean at this point, i.e., %UBIFS_MST_DIRTY is not set.
+ * E.g., consider the following chain of events:
+ *
+ * 1. UBIFS was cleanly unmounted, so the master node is clean
+ * 2. UBIFS is being mounted R/W and starts changing the master
+ * node in the first (%UBIFS_MST_LNUM). A power cut happens,
+ * so this LEB ends up with some amount of garbage at the
+ * end.
+ * 3. UBIFS is being mounted R/O. We reach this place and
+ * recover the master node from the second LEB
+ * (%UBIFS_MST_LNUM + 1). But we cannot update the media
+ * because we are being mounted R/O. We have to defer the
+ * operation.
+ * 4. However, this master node (@c->mst_node) is marked as
+ * clean (since the step 1). And if we just return, the
+ * mount code will be confused and won't recover the master
+ * node when it is re-mounter R/W later.
+ *
+ * Thus, to force the recovery by marking the master node as
+ * dirty.
+ */
+ c->mst_node->flags |= cpu_to_le32(UBIFS_MST_DIRTY);
+#ifndef __UBOOT__
+ } else {
+ /* Write the recovered master node */
+ c->max_sqnum = le64_to_cpu(mst->ch.sqnum) - 1;
+ err = write_rcvrd_mst_node(c, c->mst_node);
+ if (err)
+ goto out_free;
+#endif
+ }
+
+ vfree(buf2);
+ vfree(buf1);
+
+ return 0;
+
+out_err:
+ err = -EINVAL;
+out_free:
+ ubifs_err(c, "failed to recover master node");
+ if (mst1) {
+ ubifs_err(c, "dumping first master node");
+ ubifs_dump_node(c, mst1);
+ }
+ if (mst2) {
+ ubifs_err(c, "dumping second master node");
+ ubifs_dump_node(c, mst2);
+ }
+ vfree(buf2);
+ vfree(buf1);
+ return err;
+}
+
+/**
+ * ubifs_write_rcvrd_mst_node - write the recovered master node.
+ * @c: UBIFS file-system description object
+ *
+ * This function writes the master node that was recovered during mounting in
+ * read-only mode and must now be written because we are remounting rw.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int ubifs_write_rcvrd_mst_node(struct ubifs_info *c)
+{
+ int err;
+
+ if (!c->rcvrd_mst_node)
+ return 0;
+ c->rcvrd_mst_node->flags |= cpu_to_le32(UBIFS_MST_DIRTY);
+ c->mst_node->flags |= cpu_to_le32(UBIFS_MST_DIRTY);
+ err = write_rcvrd_mst_node(c, c->rcvrd_mst_node);
+ if (err)
+ return err;
+ kfree(c->rcvrd_mst_node);
+ c->rcvrd_mst_node = NULL;
+ return 0;
+}
+
+/**
+ * is_last_write - determine if an offset was in the last write to a LEB.
+ * @c: UBIFS file-system description object
+ * @buf: buffer to check
+ * @offs: offset to check
+ *
+ * This function returns %1 if @offs was in the last write to the LEB whose data
+ * is in @buf, otherwise %0 is returned. The determination is made by checking
+ * for subsequent empty space starting from the next @c->max_write_size
+ * boundary.
+ */
+static int is_last_write(const struct ubifs_info *c, void *buf, int offs)
+{
+ int empty_offs, check_len;
+ uint8_t *p;
+
+ /*
+ * Round up to the next @c->max_write_size boundary i.e. @offs is in
+ * the last wbuf written. After that should be empty space.
+ */
+ empty_offs = ALIGN(offs + 1, c->max_write_size);
+ check_len = c->leb_size - empty_offs;
+ p = buf + empty_offs - offs;
+ return is_empty(p, check_len);
+}
+
+/**
+ * clean_buf - clean the data from an LEB sitting in a buffer.
+ * @c: UBIFS file-system description object
+ * @buf: buffer to clean
+ * @lnum: LEB number to clean
+ * @offs: offset from which to clean
+ * @len: length of buffer
+ *
+ * This function pads up to the next min_io_size boundary (if there is one) and
+ * sets empty space to all 0xff. @buf, @offs and @len are updated to the next
+ * @c->min_io_size boundary.
+ */
+static void clean_buf(const struct ubifs_info *c, void **buf, int lnum,
+ int *offs, int *len)
+{
+ int empty_offs, pad_len;
+
+ lnum = lnum;
+ dbg_rcvry("cleaning corruption at %d:%d", lnum, *offs);
+
+ ubifs_assert(!(*offs & 7));
+ empty_offs = ALIGN(*offs, c->min_io_size);
+ pad_len = empty_offs - *offs;
+ ubifs_pad(c, *buf, pad_len);
+ *offs += pad_len;
+ *buf += pad_len;
+ *len -= pad_len;
+ memset(*buf, 0xff, c->leb_size - empty_offs);
+}
+
+/**
+ * no_more_nodes - determine if there are no more nodes in a buffer.
+ * @c: UBIFS file-system description object
+ * @buf: buffer to check
+ * @len: length of buffer
+ * @lnum: LEB number of the LEB from which @buf was read
+ * @offs: offset from which @buf was read
+ *
+ * This function ensures that the corrupted node at @offs is the last thing
+ * written to a LEB. This function returns %1 if more data is not found and
+ * %0 if more data is found.
+ */
+static int no_more_nodes(const struct ubifs_info *c, void *buf, int len,
+ int lnum, int offs)
+{
+ struct ubifs_ch *ch = buf;
+ int skip, dlen = le32_to_cpu(ch->len);
+
+ /* Check for empty space after the corrupt node's common header */
+ skip = ALIGN(offs + UBIFS_CH_SZ, c->max_write_size) - offs;
+ if (is_empty(buf + skip, len - skip))
+ return 1;
+ /*
+ * The area after the common header size is not empty, so the common
+ * header must be intact. Check it.
+ */
+ if (ubifs_check_node(c, buf, lnum, offs, 1, 0) != -EUCLEAN) {
+ dbg_rcvry("unexpected bad common header at %d:%d", lnum, offs);
+ return 0;
+ }
+ /* Now we know the corrupt node's length we can skip over it */
+ skip = ALIGN(offs + dlen, c->max_write_size) - offs;
+ /* After which there should be empty space */
+ if (is_empty(buf + skip, len - skip))
+ return 1;
+ dbg_rcvry("unexpected data at %d:%d", lnum, offs + skip);
+ return 0;
+}
+
+/**
+ * fix_unclean_leb - fix an unclean LEB.
+ * @c: UBIFS file-system description object
+ * @sleb: scanned LEB information
+ * @start: offset where scan started
+ */
+static int fix_unclean_leb(struct ubifs_info *c, struct ubifs_scan_leb *sleb,
+ int start)
+{
+ int lnum = sleb->lnum, endpt = start;
+
+ /* Get the end offset of the last node we are keeping */
+ if (!list_empty(&sleb->nodes)) {
+ struct ubifs_scan_node *snod;
+
+ snod = list_entry(sleb->nodes.prev,
+ struct ubifs_scan_node, list);
+ endpt = snod->offs + snod->len;
+ }
+
+ if (c->ro_mount && !c->remounting_rw) {
+ /* Add to recovery list */
+ struct ubifs_unclean_leb *ucleb;
+
+ dbg_rcvry("need to fix LEB %d start %d endpt %d",
+ lnum, start, sleb->endpt);
+ ucleb = kzalloc(sizeof(struct ubifs_unclean_leb), GFP_NOFS);
+ if (!ucleb)
+ return -ENOMEM;
+ ucleb->lnum = lnum;
+ ucleb->endpt = endpt;
+ list_add_tail(&ucleb->list, &c->unclean_leb_list);
+#ifndef __UBOOT__
+ } else {
+ /* Write the fixed LEB back to flash */
+ int err;
+
+ dbg_rcvry("fixing LEB %d start %d endpt %d",
+ lnum, start, sleb->endpt);
+ if (endpt == 0) {
+ err = ubifs_leb_unmap(c, lnum);
+ if (err)
+ return err;
+ } else {
+ int len = ALIGN(endpt, c->min_io_size);
+
+ if (start) {
+ err = ubifs_leb_read(c, lnum, sleb->buf, 0,
+ start, 1);
+ if (err)
+ return err;
+ }
+ /* Pad to min_io_size */
+ if (len > endpt) {
+ int pad_len = len - ALIGN(endpt, 8);
+
+ if (pad_len > 0) {
+ void *buf = sleb->buf + len - pad_len;
+
+ ubifs_pad(c, buf, pad_len);
+ }
+ }
+ err = ubifs_leb_change(c, lnum, sleb->buf, len);
+ if (err)
+ return err;
+ }
+#endif
+ }
+ return 0;
+}
+
+/**
+ * drop_last_group - drop the last group of nodes.
+ * @sleb: scanned LEB information
+ * @offs: offset of dropped nodes is returned here
+ *
+ * This is a helper function for 'ubifs_recover_leb()' which drops the last
+ * group of nodes of the scanned LEB.
+ */
+static void drop_last_group(struct ubifs_scan_leb *sleb, int *offs)
+{
+ while (!list_empty(&sleb->nodes)) {
+ struct ubifs_scan_node *snod;
+ struct ubifs_ch *ch;
+
+ snod = list_entry(sleb->nodes.prev, struct ubifs_scan_node,
+ list);
+ ch = snod->node;
+ if (ch->group_type != UBIFS_IN_NODE_GROUP)
+ break;
+
+ dbg_rcvry("dropping grouped node at %d:%d",
+ sleb->lnum, snod->offs);
+ *offs = snod->offs;
+ list_del(&snod->list);
+ kfree(snod);
+ sleb->nodes_cnt -= 1;
+ }
+}
+
+/**
+ * drop_last_node - drop the last node.
+ * @sleb: scanned LEB information
+ * @offs: offset of dropped nodes is returned here
+ *
+ * This is a helper function for 'ubifs_recover_leb()' which drops the last
+ * node of the scanned LEB.
+ */
+static void drop_last_node(struct ubifs_scan_leb *sleb, int *offs)
+{
+ struct ubifs_scan_node *snod;
+
+ if (!list_empty(&sleb->nodes)) {
+ snod = list_entry(sleb->nodes.prev, struct ubifs_scan_node,
+ list);
+
+ dbg_rcvry("dropping last node at %d:%d",
+ sleb->lnum, snod->offs);
+ *offs = snod->offs;
+ list_del(&snod->list);
+ kfree(snod);
+ sleb->nodes_cnt -= 1;
+ }
+}
+
+/**
+ * ubifs_recover_leb - scan and recover a LEB.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number
+ * @offs: offset
+ * @sbuf: LEB-sized buffer to use
+ * @jhead: journal head number this LEB belongs to (%-1 if the LEB does not
+ * belong to any journal head)
+ *
+ * This function does a scan of a LEB, but caters for errors that might have
+ * been caused by the unclean unmount from which we are attempting to recover.
+ * Returns the scanned information on success and a negative error code on
+ * failure.
+ */
+struct ubifs_scan_leb *ubifs_recover_leb(struct ubifs_info *c, int lnum,
+ int offs, void *sbuf, int jhead)
+{
+ int ret = 0, err, len = c->leb_size - offs, start = offs, min_io_unit;
+ int grouped = jhead == -1 ? 0 : c->jheads[jhead].grouped;
+ struct ubifs_scan_leb *sleb;
+ void *buf = sbuf + offs;
+
+ dbg_rcvry("%d:%d, jhead %d, grouped %d", lnum, offs, jhead, grouped);
+
+ sleb = ubifs_start_scan(c, lnum, offs, sbuf);
+ if (IS_ERR(sleb))
+ return sleb;
+
+ ubifs_assert(len >= 8);
+ while (len >= 8) {
+ dbg_scan("look at LEB %d:%d (%d bytes left)",
+ lnum, offs, len);
+
+ cond_resched();
+
+ /*
+ * Scan quietly until there is an error from which we cannot
+ * recover
+ */
+ ret = ubifs_scan_a_node(c, buf, len, lnum, offs, 1);
+ if (ret == SCANNED_A_NODE) {
+ /* A valid node, and not a padding node */
+ struct ubifs_ch *ch = buf;
+ int node_len;
+
+ err = ubifs_add_snod(c, sleb, buf, offs);
+ if (err)
+ goto error;
+ node_len = ALIGN(le32_to_cpu(ch->len), 8);
+ offs += node_len;
+ buf += node_len;
+ len -= node_len;
+ } else if (ret > 0) {
+ /* Padding bytes or a valid padding node */
+ offs += ret;
+ buf += ret;
+ len -= ret;
+ } else if (ret == SCANNED_EMPTY_SPACE ||
+ ret == SCANNED_GARBAGE ||
+ ret == SCANNED_A_BAD_PAD_NODE ||
+ ret == SCANNED_A_CORRUPT_NODE) {
+ dbg_rcvry("found corruption (%d) at %d:%d",
+ ret, lnum, offs);
+ break;
+ } else {
+ ubifs_err(c, "unexpected return value %d", ret);
+ err = -EINVAL;
+ goto error;
+ }
+ }
+
+ if (ret == SCANNED_GARBAGE || ret == SCANNED_A_BAD_PAD_NODE) {
+ if (!is_last_write(c, buf, offs))
+ goto corrupted_rescan;
+ } else if (ret == SCANNED_A_CORRUPT_NODE) {
+ if (!no_more_nodes(c, buf, len, lnum, offs))
+ goto corrupted_rescan;
+ } else if (!is_empty(buf, len)) {
+ if (!is_last_write(c, buf, offs)) {
+ int corruption = first_non_ff(buf, len);
+
+ /*
+ * See header comment for this file for more
+ * explanations about the reasons we have this check.
+ */
+ ubifs_err(c, "corrupt empty space LEB %d:%d, corruption starts at %d",
+ lnum, offs, corruption);
+ /* Make sure we dump interesting non-0xFF data */
+ offs += corruption;
+ buf += corruption;
+ goto corrupted;
+ }
+ }
+
+ min_io_unit = round_down(offs, c->min_io_size);
+ if (grouped)
+ /*
+ * If nodes are grouped, always drop the incomplete group at
+ * the end.
+ */
+ drop_last_group(sleb, &offs);
+
+ if (jhead == GCHD) {
+ /*
+ * If this LEB belongs to the GC head then while we are in the
+ * middle of the same min. I/O unit keep dropping nodes. So
+ * basically, what we want is to make sure that the last min.
+ * I/O unit where we saw the corruption is dropped completely
+ * with all the uncorrupted nodes which may possibly sit there.
+ *
+ * In other words, let's name the min. I/O unit where the
+ * corruption starts B, and the previous min. I/O unit A. The
+ * below code tries to deal with a situation when half of B
+ * contains valid nodes or the end of a valid node, and the
+ * second half of B contains corrupted data or garbage. This
+ * means that UBIFS had been writing to B just before the power
+ * cut happened. I do not know how realistic is this scenario
+ * that half of the min. I/O unit had been written successfully
+ * and the other half not, but this is possible in our 'failure
+ * mode emulation' infrastructure at least.
+ *
+ * So what is the problem, why we need to drop those nodes? Why
+ * can't we just clean-up the second half of B by putting a
+ * padding node there? We can, and this works fine with one
+ * exception which was reproduced with power cut emulation
+ * testing and happens extremely rarely.
+ *
+ * Imagine the file-system is full, we run GC which starts
+ * moving valid nodes from LEB X to LEB Y (obviously, LEB Y is
+ * the current GC head LEB). The @c->gc_lnum is -1, which means
+ * that GC will retain LEB X and will try to continue. Imagine
+ * that LEB X is currently the dirtiest LEB, and the amount of
+ * used space in LEB Y is exactly the same as amount of free
+ * space in LEB X.
+ *
+ * And a power cut happens when nodes are moved from LEB X to
+ * LEB Y. We are here trying to recover LEB Y which is the GC
+ * head LEB. We find the min. I/O unit B as described above.
+ * Then we clean-up LEB Y by padding min. I/O unit. And later
+ * 'ubifs_rcvry_gc_commit()' function fails, because it cannot
+ * find a dirty LEB which could be GC'd into LEB Y! Even LEB X
+ * does not match because the amount of valid nodes there does
+ * not fit the free space in LEB Y any more! And this is
+ * because of the padding node which we added to LEB Y. The
+ * user-visible effect of this which I once observed and
+ * analysed is that we cannot mount the file-system with
+ * -ENOSPC error.
+ *
+ * So obviously, to make sure that situation does not happen we
+ * should free min. I/O unit B in LEB Y completely and the last
+ * used min. I/O unit in LEB Y should be A. This is basically
+ * what the below code tries to do.
+ */
+ while (offs > min_io_unit)
+ drop_last_node(sleb, &offs);
+ }
+
+ buf = sbuf + offs;
+ len = c->leb_size - offs;
+
+ clean_buf(c, &buf, lnum, &offs, &len);
+ ubifs_end_scan(c, sleb, lnum, offs);
+
+ err = fix_unclean_leb(c, sleb, start);
+ if (err)
+ goto error;
+
+ return sleb;
+
+corrupted_rescan:
+ /* Re-scan the corrupted data with verbose messages */
+ ubifs_err(c, "corruption %d", ret);
+ ubifs_scan_a_node(c, buf, len, lnum, offs, 1);
+corrupted:
+ ubifs_scanned_corruption(c, lnum, offs, buf);
+ err = -EUCLEAN;
+error:
+ ubifs_err(c, "LEB %d scanning failed", lnum);
+ ubifs_scan_destroy(sleb);
+ return ERR_PTR(err);
+}
+
+/**
+ * get_cs_sqnum - get commit start sequence number.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number of commit start node
+ * @offs: offset of commit start node
+ * @cs_sqnum: commit start sequence number is returned here
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int get_cs_sqnum(struct ubifs_info *c, int lnum, int offs,
+ unsigned long long *cs_sqnum)
+{
+ struct ubifs_cs_node *cs_node = NULL;
+ int err, ret;
+
+ dbg_rcvry("at %d:%d", lnum, offs);
+ cs_node = kmalloc(UBIFS_CS_NODE_SZ, GFP_KERNEL);
+ if (!cs_node)
+ return -ENOMEM;
+ if (c->leb_size - offs < UBIFS_CS_NODE_SZ)
+ goto out_err;
+ err = ubifs_leb_read(c, lnum, (void *)cs_node, offs,
+ UBIFS_CS_NODE_SZ, 0);
+ if (err && err != -EBADMSG)
+ goto out_free;
+ ret = ubifs_scan_a_node(c, cs_node, UBIFS_CS_NODE_SZ, lnum, offs, 0);
+ if (ret != SCANNED_A_NODE) {
+ ubifs_err(c, "Not a valid node");
+ goto out_err;
+ }
+ if (cs_node->ch.node_type != UBIFS_CS_NODE) {
+ ubifs_err(c, "Node a CS node, type is %d", cs_node->ch.node_type);
+ goto out_err;
+ }
+ if (le64_to_cpu(cs_node->cmt_no) != c->cmt_no) {
+ ubifs_err(c, "CS node cmt_no %llu != current cmt_no %llu",
+ (unsigned long long)le64_to_cpu(cs_node->cmt_no),
+ c->cmt_no);
+ goto out_err;
+ }
+ *cs_sqnum = le64_to_cpu(cs_node->ch.sqnum);
+ dbg_rcvry("commit start sqnum %llu", *cs_sqnum);
+ kfree(cs_node);
+ return 0;
+
+out_err:
+ err = -EINVAL;
+out_free:
+ ubifs_err(c, "failed to get CS sqnum");
+ kfree(cs_node);
+ return err;
+}
+
+/**
+ * ubifs_recover_log_leb - scan and recover a log LEB.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number
+ * @offs: offset
+ * @sbuf: LEB-sized buffer to use
+ *
+ * This function does a scan of a LEB, but caters for errors that might have
+ * been caused by unclean reboots from which we are attempting to recover
+ * (assume that only the last log LEB can be corrupted by an unclean reboot).
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+struct ubifs_scan_leb *ubifs_recover_log_leb(struct ubifs_info *c, int lnum,
+ int offs, void *sbuf)
+{
+ struct ubifs_scan_leb *sleb;
+ int next_lnum;
+
+ dbg_rcvry("LEB %d", lnum);
+ next_lnum = lnum + 1;
+ if (next_lnum >= UBIFS_LOG_LNUM + c->log_lebs)
+ next_lnum = UBIFS_LOG_LNUM;
+ if (next_lnum != c->ltail_lnum) {
+ /*
+ * We can only recover at the end of the log, so check that the
+ * next log LEB is empty or out of date.
+ */
+ sleb = ubifs_scan(c, next_lnum, 0, sbuf, 0);
+ if (IS_ERR(sleb))
+ return sleb;
+ if (sleb->nodes_cnt) {
+ struct ubifs_scan_node *snod;
+ unsigned long long cs_sqnum = c->cs_sqnum;
+
+ snod = list_entry(sleb->nodes.next,
+ struct ubifs_scan_node, list);
+ if (cs_sqnum == 0) {
+ int err;
+
+ err = get_cs_sqnum(c, lnum, offs, &cs_sqnum);
+ if (err) {
+ ubifs_scan_destroy(sleb);
+ return ERR_PTR(err);
+ }
+ }
+ if (snod->sqnum > cs_sqnum) {
+ ubifs_err(c, "unrecoverable log corruption in LEB %d",
+ lnum);
+ ubifs_scan_destroy(sleb);
+ return ERR_PTR(-EUCLEAN);
+ }
+ }
+ ubifs_scan_destroy(sleb);
+ }
+ return ubifs_recover_leb(c, lnum, offs, sbuf, -1);
+}
+
+/**
+ * recover_head - recover a head.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number of head to recover
+ * @offs: offset of head to recover
+ * @sbuf: LEB-sized buffer to use
+ *
+ * This function ensures that there is no data on the flash at a head location.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int recover_head(struct ubifs_info *c, int lnum, int offs, void *sbuf)
+{
+ int len = c->max_write_size, err;
+
+ if (offs + len > c->leb_size)
+ len = c->leb_size - offs;
+
+ if (!len)
+ return 0;
+
+ /* Read at the head location and check it is empty flash */
+ err = ubifs_leb_read(c, lnum, sbuf, offs, len, 1);
+ if (err || !is_empty(sbuf, len)) {
+ dbg_rcvry("cleaning head at %d:%d", lnum, offs);
+ if (offs == 0)
+ return ubifs_leb_unmap(c, lnum);
+ err = ubifs_leb_read(c, lnum, sbuf, 0, offs, 1);
+ if (err)
+ return err;
+ return ubifs_leb_change(c, lnum, sbuf, offs);
+ }
+
+ return 0;
+}
+
+/**
+ * ubifs_recover_inl_heads - recover index and LPT heads.
+ * @c: UBIFS file-system description object
+ * @sbuf: LEB-sized buffer to use
+ *
+ * This function ensures that there is no data on the flash at the index and
+ * LPT head locations.
+ *
+ * This deals with the recovery of a half-completed journal commit. UBIFS is
+ * careful never to overwrite the last version of the index or the LPT. Because
+ * the index and LPT are wandering trees, data from a half-completed commit will
+ * not be referenced anywhere in UBIFS. The data will be either in LEBs that are
+ * assumed to be empty and will be unmapped anyway before use, or in the index
+ * and LPT heads.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int ubifs_recover_inl_heads(struct ubifs_info *c, void *sbuf)
+{
+ int err;
+
+ ubifs_assert(!c->ro_mount || c->remounting_rw);
+
+ dbg_rcvry("checking index head at %d:%d", c->ihead_lnum, c->ihead_offs);
+ err = recover_head(c, c->ihead_lnum, c->ihead_offs, sbuf);
+ if (err)
+ return err;
+
+ dbg_rcvry("checking LPT head at %d:%d", c->nhead_lnum, c->nhead_offs);
+
+ return recover_head(c, c->nhead_lnum, c->nhead_offs, sbuf);
+}
+
+/**
+ * clean_an_unclean_leb - read and write a LEB to remove corruption.
+ * @c: UBIFS file-system description object
+ * @ucleb: unclean LEB information
+ * @sbuf: LEB-sized buffer to use
+ *
+ * This function reads a LEB up to a point pre-determined by the mount recovery,
+ * checks the nodes, and writes the result back to the flash, thereby cleaning
+ * off any following corruption, or non-fatal ECC errors.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int clean_an_unclean_leb(struct ubifs_info *c,
+ struct ubifs_unclean_leb *ucleb, void *sbuf)
+{
+ int err, lnum = ucleb->lnum, offs = 0, len = ucleb->endpt, quiet = 1;
+ void *buf = sbuf;
+
+ dbg_rcvry("LEB %d len %d", lnum, len);
+
+ if (len == 0) {
+ /* Nothing to read, just unmap it */
+ return ubifs_leb_unmap(c, lnum);
+ }
+
+ err = ubifs_leb_read(c, lnum, buf, offs, len, 0);
+ if (err && err != -EBADMSG)
+ return err;
+
+ while (len >= 8) {
+ int ret;
+
+ cond_resched();
+
+ /* Scan quietly until there is an error */
+ ret = ubifs_scan_a_node(c, buf, len, lnum, offs, quiet);
+
+ if (ret == SCANNED_A_NODE) {
+ /* A valid node, and not a padding node */
+ struct ubifs_ch *ch = buf;
+ int node_len;
+
+ node_len = ALIGN(le32_to_cpu(ch->len), 8);
+ offs += node_len;
+ buf += node_len;
+ len -= node_len;
+ continue;
+ }
+
+ if (ret > 0) {
+ /* Padding bytes or a valid padding node */
+ offs += ret;
+ buf += ret;
+ len -= ret;
+ continue;
+ }
+
+ if (ret == SCANNED_EMPTY_SPACE) {
+ ubifs_err(c, "unexpected empty space at %d:%d",
+ lnum, offs);
+ return -EUCLEAN;
+ }
+
+ if (quiet) {
+ /* Redo the last scan but noisily */
+ quiet = 0;
+ continue;
+ }
+
+ ubifs_scanned_corruption(c, lnum, offs, buf);
+ return -EUCLEAN;
+ }
+
+ /* Pad to min_io_size */
+ len = ALIGN(ucleb->endpt, c->min_io_size);
+ if (len > ucleb->endpt) {
+ int pad_len = len - ALIGN(ucleb->endpt, 8);
+
+ if (pad_len > 0) {
+ buf = c->sbuf + len - pad_len;
+ ubifs_pad(c, buf, pad_len);
+ }
+ }
+
+ /* Write back the LEB atomically */
+ err = ubifs_leb_change(c, lnum, sbuf, len);
+ if (err)
+ return err;
+
+ dbg_rcvry("cleaned LEB %d", lnum);
+
+ return 0;
+}
+
+/**
+ * ubifs_clean_lebs - clean LEBs recovered during read-only mount.
+ * @c: UBIFS file-system description object
+ * @sbuf: LEB-sized buffer to use
+ *
+ * This function cleans a LEB identified during recovery that needs to be
+ * written but was not because UBIFS was mounted read-only. This happens when
+ * remounting to read-write mode.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int ubifs_clean_lebs(struct ubifs_info *c, void *sbuf)
+{
+ dbg_rcvry("recovery");
+ while (!list_empty(&c->unclean_leb_list)) {
+ struct ubifs_unclean_leb *ucleb;
+ int err;
+
+ ucleb = list_entry(c->unclean_leb_list.next,
+ struct ubifs_unclean_leb, list);
+ err = clean_an_unclean_leb(c, ucleb, sbuf);
+ if (err)
+ return err;
+ list_del(&ucleb->list);
+ kfree(ucleb);
+ }
+ return 0;
+}
+
+#ifndef __UBOOT__
+/**
+ * grab_empty_leb - grab an empty LEB to use as GC LEB and run commit.
+ * @c: UBIFS file-system description object
+ *
+ * This is a helper function for 'ubifs_rcvry_gc_commit()' which grabs an empty
+ * LEB to be used as GC LEB (@c->gc_lnum), and then runs the commit. Returns
+ * zero in case of success and a negative error code in case of failure.
+ */
+static int grab_empty_leb(struct ubifs_info *c)
+{
+ int lnum, err;
+
+ /*
+ * Note, it is very important to first search for an empty LEB and then
+ * run the commit, not vice-versa. The reason is that there might be
+ * only one empty LEB at the moment, the one which has been the
+ * @c->gc_lnum just before the power cut happened. During the regular
+ * UBIFS operation (not now) @c->gc_lnum is marked as "taken", so no
+ * one but GC can grab it. But at this moment this single empty LEB is
+ * not marked as taken, so if we run commit - what happens? Right, the
+ * commit will grab it and write the index there. Remember that the
+ * index always expands as long as there is free space, and it only
+ * starts consolidating when we run out of space.
+ *
+ * IOW, if we run commit now, we might not be able to find a free LEB
+ * after this.
+ */
+ lnum = ubifs_find_free_leb_for_idx(c);
+ if (lnum < 0) {
+ ubifs_err(c, "could not find an empty LEB");
+ ubifs_dump_lprops(c);
+ ubifs_dump_budg(c, &c->bi);
+ return lnum;
+ }
+
+ /* Reset the index flag */
+ err = ubifs_change_one_lp(c, lnum, LPROPS_NC, LPROPS_NC, 0,
+ LPROPS_INDEX, 0);
+ if (err)
+ return err;
+
+ c->gc_lnum = lnum;
+ dbg_rcvry("found empty LEB %d, run commit", lnum);
+
+ return ubifs_run_commit(c);
+}
+
+/**
+ * ubifs_rcvry_gc_commit - recover the GC LEB number and run the commit.
+ * @c: UBIFS file-system description object
+ *
+ * Out-of-place garbage collection requires always one empty LEB with which to
+ * start garbage collection. The LEB number is recorded in c->gc_lnum and is
+ * written to the master node on unmounting. In the case of an unclean unmount
+ * the value of gc_lnum recorded in the master node is out of date and cannot
+ * be used. Instead, recovery must allocate an empty LEB for this purpose.
+ * However, there may not be enough empty space, in which case it must be
+ * possible to GC the dirtiest LEB into the GC head LEB.
+ *
+ * This function also runs the commit which causes the TNC updates from
+ * size-recovery and orphans to be written to the flash. That is important to
+ * ensure correct replay order for subsequent mounts.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int ubifs_rcvry_gc_commit(struct ubifs_info *c)
+{
+ struct ubifs_wbuf *wbuf = &c->jheads[GCHD].wbuf;
+ struct ubifs_lprops lp;
+ int err;
+
+ dbg_rcvry("GC head LEB %d, offs %d", wbuf->lnum, wbuf->offs);
+
+ c->gc_lnum = -1;
+ if (wbuf->lnum == -1 || wbuf->offs == c->leb_size)
+ return grab_empty_leb(c);
+
+ err = ubifs_find_dirty_leb(c, &lp, wbuf->offs, 2);
+ if (err) {
+ if (err != -ENOSPC)
+ return err;
+
+ dbg_rcvry("could not find a dirty LEB");
+ return grab_empty_leb(c);
+ }
+
+ ubifs_assert(!(lp.flags & LPROPS_INDEX));
+ ubifs_assert(lp.free + lp.dirty >= wbuf->offs);
+
+ /*
+ * We run the commit before garbage collection otherwise subsequent
+ * mounts will see the GC and orphan deletion in a different order.
+ */
+ dbg_rcvry("committing");
+ err = ubifs_run_commit(c);
+ if (err)
+ return err;
+
+ dbg_rcvry("GC'ing LEB %d", lp.lnum);
+ mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead);
+ err = ubifs_garbage_collect_leb(c, &lp);
+ if (err >= 0) {
+ int err2 = ubifs_wbuf_sync_nolock(wbuf);
+
+ if (err2)
+ err = err2;
+ }
+ mutex_unlock(&wbuf->io_mutex);
+ if (err < 0) {
+ ubifs_err(c, "GC failed, error %d", err);
+ if (err == -EAGAIN)
+ err = -EINVAL;
+ return err;
+ }
+
+ ubifs_assert(err == LEB_RETAINED);
+ if (err != LEB_RETAINED)
+ return -EINVAL;
+
+ err = ubifs_leb_unmap(c, c->gc_lnum);
+ if (err)
+ return err;
+
+ dbg_rcvry("allocated LEB %d for GC", lp.lnum);
+ return 0;
+}
+#else
+int ubifs_rcvry_gc_commit(struct ubifs_info *c)
+{
+ return 0;
+}
+#endif
+
+/**
+ * struct size_entry - inode size information for recovery.
+ * @rb: link in the RB-tree of sizes
+ * @inum: inode number
+ * @i_size: size on inode
+ * @d_size: maximum size based on data nodes
+ * @exists: indicates whether the inode exists
+ * @inode: inode if pinned in memory awaiting rw mode to fix it
+ */
+struct size_entry {
+ struct rb_node rb;
+ ino_t inum;
+ loff_t i_size;
+ loff_t d_size;
+ int exists;
+ struct inode *inode;
+};
+
+/**
+ * add_ino - add an entry to the size tree.
+ * @c: UBIFS file-system description object
+ * @inum: inode number
+ * @i_size: size on inode
+ * @d_size: maximum size based on data nodes
+ * @exists: indicates whether the inode exists
+ */
+static int add_ino(struct ubifs_info *c, ino_t inum, loff_t i_size,
+ loff_t d_size, int exists)
+{
+ struct rb_node **p = &c->size_tree.rb_node, *parent = NULL;
+ struct size_entry *e;
+
+ while (*p) {
+ parent = *p;
+ e = rb_entry(parent, struct size_entry, rb);
+ if (inum < e->inum)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+
+ e = kzalloc(sizeof(struct size_entry), GFP_KERNEL);
+ if (!e)
+ return -ENOMEM;
+
+ e->inum = inum;
+ e->i_size = i_size;
+ e->d_size = d_size;
+ e->exists = exists;
+
+ rb_link_node(&e->rb, parent, p);
+ rb_insert_color(&e->rb, &c->size_tree);
+
+ return 0;
+}
+
+/**
+ * find_ino - find an entry on the size tree.
+ * @c: UBIFS file-system description object
+ * @inum: inode number
+ */
+static struct size_entry *find_ino(struct ubifs_info *c, ino_t inum)
+{
+ struct rb_node *p = c->size_tree.rb_node;
+ struct size_entry *e;
+
+ while (p) {
+ e = rb_entry(p, struct size_entry, rb);
+ if (inum < e->inum)
+ p = p->rb_left;
+ else if (inum > e->inum)
+ p = p->rb_right;
+ else
+ return e;
+ }
+ return NULL;
+}
+
+/**
+ * remove_ino - remove an entry from the size tree.
+ * @c: UBIFS file-system description object
+ * @inum: inode number
+ */
+static void remove_ino(struct ubifs_info *c, ino_t inum)
+{
+ struct size_entry *e = find_ino(c, inum);
+
+ if (!e)
+ return;
+ rb_erase(&e->rb, &c->size_tree);
+ kfree(e);
+}
+
+/**
+ * ubifs_destroy_size_tree - free resources related to the size tree.
+ * @c: UBIFS file-system description object
+ */
+void ubifs_destroy_size_tree(struct ubifs_info *c)
+{
+ struct size_entry *e, *n;
+
+ rbtree_postorder_for_each_entry_safe(e, n, &c->size_tree, rb) {
+ if (e->inode)
+ iput(e->inode);
+ kfree(e);
+ }
+
+ c->size_tree = RB_ROOT;
+}
+
+/**
+ * ubifs_recover_size_accum - accumulate inode sizes for recovery.
+ * @c: UBIFS file-system description object
+ * @key: node key
+ * @deletion: node is for a deletion
+ * @new_size: inode size
+ *
+ * This function has two purposes:
+ * 1) to ensure there are no data nodes that fall outside the inode size
+ * 2) to ensure there are no data nodes for inodes that do not exist
+ * To accomplish those purposes, a rb-tree is constructed containing an entry
+ * for each inode number in the journal that has not been deleted, and recording
+ * the size from the inode node, the maximum size of any data node (also altered
+ * by truncations) and a flag indicating a inode number for which no inode node
+ * was present in the journal.
+ *
+ * Note that there is still the possibility that there are data nodes that have
+ * been committed that are beyond the inode size, however the only way to find
+ * them would be to scan the entire index. Alternatively, some provision could
+ * be made to record the size of inodes at the start of commit, which would seem
+ * very cumbersome for a scenario that is quite unlikely and the only negative
+ * consequence of which is wasted space.
+ *
+ * This functions returns %0 on success and a negative error code on failure.
+ */
+int ubifs_recover_size_accum(struct ubifs_info *c, union ubifs_key *key,
+ int deletion, loff_t new_size)
+{
+ ino_t inum = key_inum(c, key);
+ struct size_entry *e;
+ int err;
+
+ switch (key_type(c, key)) {
+ case UBIFS_INO_KEY:
+ if (deletion)
+ remove_ino(c, inum);
+ else {
+ e = find_ino(c, inum);
+ if (e) {
+ e->i_size = new_size;
+ e->exists = 1;
+ } else {
+ err = add_ino(c, inum, new_size, 0, 1);
+ if (err)
+ return err;
+ }
+ }
+ break;
+ case UBIFS_DATA_KEY:
+ e = find_ino(c, inum);
+ if (e) {
+ if (new_size > e->d_size)
+ e->d_size = new_size;
+ } else {
+ err = add_ino(c, inum, 0, new_size, 0);
+ if (err)
+ return err;
+ }
+ break;
+ case UBIFS_TRUN_KEY:
+ e = find_ino(c, inum);
+ if (e)
+ e->d_size = new_size;
+ break;
+ }
+ return 0;
+}
+
+#ifndef __UBOOT__
+/**
+ * fix_size_in_place - fix inode size in place on flash.
+ * @c: UBIFS file-system description object
+ * @e: inode size information for recovery
+ */
+static int fix_size_in_place(struct ubifs_info *c, struct size_entry *e)
+{
+ struct ubifs_ino_node *ino = c->sbuf;
+ unsigned char *p;
+ union ubifs_key key;
+ int err, lnum, offs, len;
+ loff_t i_size;
+ uint32_t crc;
+
+ /* Locate the inode node LEB number and offset */
+ ino_key_init(c, &key, e->inum);
+ err = ubifs_tnc_locate(c, &key, ino, &lnum, &offs);
+ if (err)
+ goto out;
+ /*
+ * If the size recorded on the inode node is greater than the size that
+ * was calculated from nodes in the journal then don't change the inode.
+ */
+ i_size = le64_to_cpu(ino->size);
+ if (i_size >= e->d_size)
+ return 0;
+ /* Read the LEB */
+ err = ubifs_leb_read(c, lnum, c->sbuf, 0, c->leb_size, 1);
+ if (err)
+ goto out;
+ /* Change the size field and recalculate the CRC */
+ ino = c->sbuf + offs;
+ ino->size = cpu_to_le64(e->d_size);
+ len = le32_to_cpu(ino->ch.len);
+ crc = crc32(UBIFS_CRC32_INIT, (void *)ino + 8, len - 8);
+ ino->ch.crc = cpu_to_le32(crc);
+ /* Work out where data in the LEB ends and free space begins */
+ p = c->sbuf;
+ len = c->leb_size - 1;
+ while (p[len] == 0xff)
+ len -= 1;
+ len = ALIGN(len + 1, c->min_io_size);
+ /* Atomically write the fixed LEB back again */
+ err = ubifs_leb_change(c, lnum, c->sbuf, len);
+ if (err)
+ goto out;
+ dbg_rcvry("inode %lu at %d:%d size %lld -> %lld",
+ (unsigned long)e->inum, lnum, offs, i_size, e->d_size);
+ return 0;
+
+out:
+ ubifs_warn(c, "inode %lu failed to fix size %lld -> %lld error %d",
+ (unsigned long)e->inum, e->i_size, e->d_size, err);
+ return err;
+}
+#endif
+
+/**
+ * ubifs_recover_size - recover inode size.
+ * @c: UBIFS file-system description object
+ *
+ * This function attempts to fix inode size discrepancies identified by the
+ * 'ubifs_recover_size_accum()' function.
+ *
+ * This functions returns %0 on success and a negative error code on failure.
+ */
+int ubifs_recover_size(struct ubifs_info *c)
+{
+ struct rb_node *this = rb_first(&c->size_tree);
+
+ while (this) {
+ struct size_entry *e;
+ int err;
+
+ e = rb_entry(this, struct size_entry, rb);
+ if (!e->exists) {
+ union ubifs_key key;
+
+ ino_key_init(c, &key, e->inum);
+ err = ubifs_tnc_lookup(c, &key, c->sbuf);
+ if (err && err != -ENOENT)
+ return err;
+ if (err == -ENOENT) {
+ /* Remove data nodes that have no inode */
+ dbg_rcvry("removing ino %lu",
+ (unsigned long)e->inum);
+ err = ubifs_tnc_remove_ino(c, e->inum);
+ if (err)
+ return err;
+ } else {
+ struct ubifs_ino_node *ino = c->sbuf;
+
+ e->exists = 1;
+ e->i_size = le64_to_cpu(ino->size);
+ }
+ }
+
+ if (e->exists && e->i_size < e->d_size) {
+ if (c->ro_mount) {
+ /* Fix the inode size and pin it in memory */
+ struct inode *inode;
+ struct ubifs_inode *ui;
+
+ ubifs_assert(!e->inode);
+
+ inode = ubifs_iget(c->vfs_sb, e->inum);
+ if (IS_ERR(inode))
+ return PTR_ERR(inode);
+
+ ui = ubifs_inode(inode);
+ if (inode->i_size < e->d_size) {
+ dbg_rcvry("ino %lu size %lld -> %lld",
+ (unsigned long)e->inum,
+ inode->i_size, e->d_size);
+ inode->i_size = e->d_size;
+ ui->ui_size = e->d_size;
+ ui->synced_i_size = e->d_size;
+ e->inode = inode;
+ this = rb_next(this);
+ continue;
+ }
+ iput(inode);
+#ifndef __UBOOT__
+ } else {
+ /* Fix the size in place */
+ err = fix_size_in_place(c, e);
+ if (err)
+ return err;
+ if (e->inode)
+ iput(e->inode);
+#endif
+ }
+ }
+
+ this = rb_next(this);
+ rb_erase(&e->rb, &c->size_tree);
+ kfree(e);
+ }
+
+ return 0;
+}
diff --git a/roms/u-boot/fs/ubifs/replay.c b/roms/u-boot/fs/ubifs/replay.c
new file mode 100644
index 000000000..aa7f281ef
--- /dev/null
+++ b/roms/u-boot/fs/ubifs/replay.c
@@ -0,0 +1,1077 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ *
+ * Authors: Adrian Hunter
+ * Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * This file contains journal replay code. It runs when the file-system is being
+ * mounted and requires no locking.
+ *
+ * The larger is the journal, the longer it takes to scan it, so the longer it
+ * takes to mount UBIFS. This is why the journal has limited size which may be
+ * changed depending on the system requirements. But a larger journal gives
+ * faster I/O speed because it writes the index less frequently. So this is a
+ * trade-off. Also, the journal is indexed by the in-memory index (TNC), so the
+ * larger is the journal, the more memory its index may consume.
+ */
+
+#ifdef __UBOOT__
+#include <log.h>
+#include <dm/devres.h>
+#include <linux/compat.h>
+#include <linux/err.h>
+#endif
+#include "ubifs.h"
+#include <linux/bug.h>
+#include <linux/list_sort.h>
+
+/**
+ * struct replay_entry - replay list entry.
+ * @lnum: logical eraseblock number of the node
+ * @offs: node offset
+ * @len: node length
+ * @deletion: non-zero if this entry corresponds to a node deletion
+ * @sqnum: node sequence number
+ * @list: links the replay list
+ * @key: node key
+ * @nm: directory entry name
+ * @old_size: truncation old size
+ * @new_size: truncation new size
+ *
+ * The replay process first scans all buds and builds the replay list, then
+ * sorts the replay list in nodes sequence number order, and then inserts all
+ * the replay entries to the TNC.
+ */
+struct replay_entry {
+ int lnum;
+ int offs;
+ int len;
+ unsigned int deletion:1;
+ unsigned long long sqnum;
+ struct list_head list;
+ union ubifs_key key;
+ union {
+ struct qstr nm;
+ struct {
+ loff_t old_size;
+ loff_t new_size;
+ };
+ };
+};
+
+/**
+ * struct bud_entry - entry in the list of buds to replay.
+ * @list: next bud in the list
+ * @bud: bud description object
+ * @sqnum: reference node sequence number
+ * @free: free bytes in the bud
+ * @dirty: dirty bytes in the bud
+ */
+struct bud_entry {
+ struct list_head list;
+ struct ubifs_bud *bud;
+ unsigned long long sqnum;
+ int free;
+ int dirty;
+};
+
+/**
+ * set_bud_lprops - set free and dirty space used by a bud.
+ * @c: UBIFS file-system description object
+ * @b: bud entry which describes the bud
+ *
+ * This function makes sure the LEB properties of bud @b are set correctly
+ * after the replay. Returns zero in case of success and a negative error code
+ * in case of failure.
+ */
+static int set_bud_lprops(struct ubifs_info *c, struct bud_entry *b)
+{
+ const struct ubifs_lprops *lp;
+ int err = 0, dirty;
+
+ ubifs_get_lprops(c);
+
+ lp = ubifs_lpt_lookup_dirty(c, b->bud->lnum);
+ if (IS_ERR(lp)) {
+ err = PTR_ERR(lp);
+ goto out;
+ }
+
+ dirty = lp->dirty;
+ if (b->bud->start == 0 && (lp->free != c->leb_size || lp->dirty != 0)) {
+ /*
+ * The LEB was added to the journal with a starting offset of
+ * zero which means the LEB must have been empty. The LEB
+ * property values should be @lp->free == @c->leb_size and
+ * @lp->dirty == 0, but that is not the case. The reason is that
+ * the LEB had been garbage collected before it became the bud,
+ * and there was not commit inbetween. The garbage collector
+ * resets the free and dirty space without recording it
+ * anywhere except lprops, so if there was no commit then
+ * lprops does not have that information.
+ *
+ * We do not need to adjust free space because the scan has told
+ * us the exact value which is recorded in the replay entry as
+ * @b->free.
+ *
+ * However we do need to subtract from the dirty space the
+ * amount of space that the garbage collector reclaimed, which
+ * is the whole LEB minus the amount of space that was free.
+ */
+ dbg_mnt("bud LEB %d was GC'd (%d free, %d dirty)", b->bud->lnum,
+ lp->free, lp->dirty);
+ dbg_gc("bud LEB %d was GC'd (%d free, %d dirty)", b->bud->lnum,
+ lp->free, lp->dirty);
+ dirty -= c->leb_size - lp->free;
+ /*
+ * If the replay order was perfect the dirty space would now be
+ * zero. The order is not perfect because the journal heads
+ * race with each other. This is not a problem but is does mean
+ * that the dirty space may temporarily exceed c->leb_size
+ * during the replay.
+ */
+ if (dirty != 0)
+ dbg_mnt("LEB %d lp: %d free %d dirty replay: %d free %d dirty",
+ b->bud->lnum, lp->free, lp->dirty, b->free,
+ b->dirty);
+ }
+ lp = ubifs_change_lp(c, lp, b->free, dirty + b->dirty,
+ lp->flags | LPROPS_TAKEN, 0);
+ if (IS_ERR(lp)) {
+ err = PTR_ERR(lp);
+ goto out;
+ }
+
+ /* Make sure the journal head points to the latest bud */
+ err = ubifs_wbuf_seek_nolock(&c->jheads[b->bud->jhead].wbuf,
+ b->bud->lnum, c->leb_size - b->free);
+
+out:
+ ubifs_release_lprops(c);
+ return err;
+}
+
+/**
+ * set_buds_lprops - set free and dirty space for all replayed buds.
+ * @c: UBIFS file-system description object
+ *
+ * This function sets LEB properties for all replayed buds. Returns zero in
+ * case of success and a negative error code in case of failure.
+ */
+static int set_buds_lprops(struct ubifs_info *c)
+{
+ struct bud_entry *b;
+ int err;
+
+ list_for_each_entry(b, &c->replay_buds, list) {
+ err = set_bud_lprops(c, b);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+/**
+ * trun_remove_range - apply a replay entry for a truncation to the TNC.
+ * @c: UBIFS file-system description object
+ * @r: replay entry of truncation
+ */
+static int trun_remove_range(struct ubifs_info *c, struct replay_entry *r)
+{
+ unsigned min_blk, max_blk;
+ union ubifs_key min_key, max_key;
+ ino_t ino;
+
+ min_blk = r->new_size / UBIFS_BLOCK_SIZE;
+ if (r->new_size & (UBIFS_BLOCK_SIZE - 1))
+ min_blk += 1;
+
+ max_blk = r->old_size / UBIFS_BLOCK_SIZE;
+ if ((r->old_size & (UBIFS_BLOCK_SIZE - 1)) == 0)
+ max_blk -= 1;
+
+ ino = key_inum(c, &r->key);
+
+ data_key_init(c, &min_key, ino, min_blk);
+ data_key_init(c, &max_key, ino, max_blk);
+
+ return ubifs_tnc_remove_range(c, &min_key, &max_key);
+}
+
+/**
+ * apply_replay_entry - apply a replay entry to the TNC.
+ * @c: UBIFS file-system description object
+ * @r: replay entry to apply
+ *
+ * Apply a replay entry to the TNC.
+ */
+static int apply_replay_entry(struct ubifs_info *c, struct replay_entry *r)
+{
+ int err;
+
+ dbg_mntk(&r->key, "LEB %d:%d len %d deletion %d sqnum %llu key ",
+ r->lnum, r->offs, r->len, r->deletion, r->sqnum);
+
+ /* Set c->replay_sqnum to help deal with dangling branches. */
+ c->replay_sqnum = r->sqnum;
+
+ if (is_hash_key(c, &r->key)) {
+ if (r->deletion)
+ err = ubifs_tnc_remove_nm(c, &r->key, &r->nm);
+ else
+ err = ubifs_tnc_add_nm(c, &r->key, r->lnum, r->offs,
+ r->len, &r->nm);
+ } else {
+ if (r->deletion)
+ switch (key_type(c, &r->key)) {
+ case UBIFS_INO_KEY:
+ {
+ ino_t inum = key_inum(c, &r->key);
+
+ err = ubifs_tnc_remove_ino(c, inum);
+ break;
+ }
+ case UBIFS_TRUN_KEY:
+ err = trun_remove_range(c, r);
+ break;
+ default:
+ err = ubifs_tnc_remove(c, &r->key);
+ break;
+ }
+ else
+ err = ubifs_tnc_add(c, &r->key, r->lnum, r->offs,
+ r->len);
+ if (err)
+ return err;
+
+ if (c->need_recovery)
+ err = ubifs_recover_size_accum(c, &r->key, r->deletion,
+ r->new_size);
+ }
+
+ return err;
+}
+
+/**
+ * replay_entries_cmp - compare 2 replay entries.
+ * @priv: UBIFS file-system description object
+ * @a: first replay entry
+ * @a: second replay entry
+ *
+ * This is a comparios function for 'list_sort()' which compares 2 replay
+ * entries @a and @b by comparing their sequence numer. Returns %1 if @a has
+ * greater sequence number and %-1 otherwise.
+ */
+static int replay_entries_cmp(void *priv, struct list_head *a,
+ struct list_head *b)
+{
+ struct replay_entry *ra, *rb;
+
+ cond_resched();
+ if (a == b)
+ return 0;
+
+ ra = list_entry(a, struct replay_entry, list);
+ rb = list_entry(b, struct replay_entry, list);
+ ubifs_assert(ra->sqnum != rb->sqnum);
+ if (ra->sqnum > rb->sqnum)
+ return 1;
+ return -1;
+}
+
+/**
+ * apply_replay_list - apply the replay list to the TNC.
+ * @c: UBIFS file-system description object
+ *
+ * Apply all entries in the replay list to the TNC. Returns zero in case of
+ * success and a negative error code in case of failure.
+ */
+static int apply_replay_list(struct ubifs_info *c)
+{
+ struct replay_entry *r;
+ int err;
+
+ list_sort(c, &c->replay_list, &replay_entries_cmp);
+
+ list_for_each_entry(r, &c->replay_list, list) {
+ cond_resched();
+
+ err = apply_replay_entry(c, r);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+/**
+ * destroy_replay_list - destroy the replay.
+ * @c: UBIFS file-system description object
+ *
+ * Destroy the replay list.
+ */
+static void destroy_replay_list(struct ubifs_info *c)
+{
+ struct replay_entry *r, *tmp;
+
+ list_for_each_entry_safe(r, tmp, &c->replay_list, list) {
+ if (is_hash_key(c, &r->key))
+ kfree(r->nm.name);
+ list_del(&r->list);
+ kfree(r);
+ }
+}
+
+/**
+ * insert_node - insert a node to the replay list
+ * @c: UBIFS file-system description object
+ * @lnum: node logical eraseblock number
+ * @offs: node offset
+ * @len: node length
+ * @key: node key
+ * @sqnum: sequence number
+ * @deletion: non-zero if this is a deletion
+ * @used: number of bytes in use in a LEB
+ * @old_size: truncation old size
+ * @new_size: truncation new size
+ *
+ * This function inserts a scanned non-direntry node to the replay list. The
+ * replay list contains @struct replay_entry elements, and we sort this list in
+ * sequence number order before applying it. The replay list is applied at the
+ * very end of the replay process. Since the list is sorted in sequence number
+ * order, the older modifications are applied first. This function returns zero
+ * in case of success and a negative error code in case of failure.
+ */
+static int insert_node(struct ubifs_info *c, int lnum, int offs, int len,
+ union ubifs_key *key, unsigned long long sqnum,
+ int deletion, int *used, loff_t old_size,
+ loff_t new_size)
+{
+ struct replay_entry *r;
+
+ dbg_mntk(key, "add LEB %d:%d, key ", lnum, offs);
+
+ if (key_inum(c, key) >= c->highest_inum)
+ c->highest_inum = key_inum(c, key);
+
+ r = kzalloc(sizeof(struct replay_entry), GFP_KERNEL);
+ if (!r)
+ return -ENOMEM;
+
+ if (!deletion)
+ *used += ALIGN(len, 8);
+ r->lnum = lnum;
+ r->offs = offs;
+ r->len = len;
+ r->deletion = !!deletion;
+ r->sqnum = sqnum;
+ key_copy(c, key, &r->key);
+ r->old_size = old_size;
+ r->new_size = new_size;
+
+ list_add_tail(&r->list, &c->replay_list);
+ return 0;
+}
+
+/**
+ * insert_dent - insert a directory entry node into the replay list.
+ * @c: UBIFS file-system description object
+ * @lnum: node logical eraseblock number
+ * @offs: node offset
+ * @len: node length
+ * @key: node key
+ * @name: directory entry name
+ * @nlen: directory entry name length
+ * @sqnum: sequence number
+ * @deletion: non-zero if this is a deletion
+ * @used: number of bytes in use in a LEB
+ *
+ * This function inserts a scanned directory entry node or an extended
+ * attribute entry to the replay list. Returns zero in case of success and a
+ * negative error code in case of failure.
+ */
+static int insert_dent(struct ubifs_info *c, int lnum, int offs, int len,
+ union ubifs_key *key, const char *name, int nlen,
+ unsigned long long sqnum, int deletion, int *used)
+{
+ struct replay_entry *r;
+ char *nbuf;
+
+ dbg_mntk(key, "add LEB %d:%d, key ", lnum, offs);
+ if (key_inum(c, key) >= c->highest_inum)
+ c->highest_inum = key_inum(c, key);
+
+ r = kzalloc(sizeof(struct replay_entry), GFP_KERNEL);
+ if (!r)
+ return -ENOMEM;
+
+ nbuf = kmalloc(nlen + 1, GFP_KERNEL);
+ if (!nbuf) {
+ kfree(r);
+ return -ENOMEM;
+ }
+
+ if (!deletion)
+ *used += ALIGN(len, 8);
+ r->lnum = lnum;
+ r->offs = offs;
+ r->len = len;
+ r->deletion = !!deletion;
+ r->sqnum = sqnum;
+ key_copy(c, key, &r->key);
+ r->nm.len = nlen;
+ memcpy(nbuf, name, nlen);
+ nbuf[nlen] = '\0';
+ r->nm.name = nbuf;
+
+ list_add_tail(&r->list, &c->replay_list);
+ return 0;
+}
+
+/**
+ * ubifs_validate_entry - validate directory or extended attribute entry node.
+ * @c: UBIFS file-system description object
+ * @dent: the node to validate
+ *
+ * This function validates directory or extended attribute entry node @dent.
+ * Returns zero if the node is all right and a %-EINVAL if not.
+ */
+int ubifs_validate_entry(struct ubifs_info *c,
+ const struct ubifs_dent_node *dent)
+{
+ int key_type = key_type_flash(c, dent->key);
+ int nlen = le16_to_cpu(dent->nlen);
+
+ if (le32_to_cpu(dent->ch.len) != nlen + UBIFS_DENT_NODE_SZ + 1 ||
+ dent->type >= UBIFS_ITYPES_CNT ||
+ nlen > UBIFS_MAX_NLEN || dent->name[nlen] != 0 ||
+ strnlen(dent->name, nlen) != nlen ||
+ le64_to_cpu(dent->inum) > MAX_INUM) {
+ ubifs_err(c, "bad %s node", key_type == UBIFS_DENT_KEY ?
+ "directory entry" : "extended attribute entry");
+ return -EINVAL;
+ }
+
+ if (key_type != UBIFS_DENT_KEY && key_type != UBIFS_XENT_KEY) {
+ ubifs_err(c, "bad key type %d", key_type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * is_last_bud - check if the bud is the last in the journal head.
+ * @c: UBIFS file-system description object
+ * @bud: bud description object
+ *
+ * This function checks if bud @bud is the last bud in its journal head. This
+ * information is then used by 'replay_bud()' to decide whether the bud can
+ * have corruptions or not. Indeed, only last buds can be corrupted by power
+ * cuts. Returns %1 if this is the last bud, and %0 if not.
+ */
+static int is_last_bud(struct ubifs_info *c, struct ubifs_bud *bud)
+{
+ struct ubifs_jhead *jh = &c->jheads[bud->jhead];
+ struct ubifs_bud *next;
+ uint32_t data;
+ int err;
+
+ if (list_is_last(&bud->list, &jh->buds_list))
+ return 1;
+
+ /*
+ * The following is a quirk to make sure we work correctly with UBIFS
+ * images used with older UBIFS.
+ *
+ * Normally, the last bud will be the last in the journal head's list
+ * of bud. However, there is one exception if the UBIFS image belongs
+ * to older UBIFS. This is fairly unlikely: one would need to use old
+ * UBIFS, then have a power cut exactly at the right point, and then
+ * try to mount this image with new UBIFS.
+ *
+ * The exception is: it is possible to have 2 buds A and B, A goes
+ * before B, and B is the last, bud B is contains no data, and bud A is
+ * corrupted at the end. The reason is that in older versions when the
+ * journal code switched the next bud (from A to B), it first added a
+ * log reference node for the new bud (B), and only after this it
+ * synchronized the write-buffer of current bud (A). But later this was
+ * changed and UBIFS started to always synchronize the write-buffer of
+ * the bud (A) before writing the log reference for the new bud (B).
+ *
+ * But because older UBIFS always synchronized A's write-buffer before
+ * writing to B, we can recognize this exceptional situation but
+ * checking the contents of bud B - if it is empty, then A can be
+ * treated as the last and we can recover it.
+ *
+ * TODO: remove this piece of code in a couple of years (today it is
+ * 16.05.2011).
+ */
+ next = list_entry(bud->list.next, struct ubifs_bud, list);
+ if (!list_is_last(&next->list, &jh->buds_list))
+ return 0;
+
+ err = ubifs_leb_read(c, next->lnum, (char *)&data, next->start, 4, 1);
+ if (err)
+ return 0;
+
+ return data == 0xFFFFFFFF;
+}
+
+/**
+ * replay_bud - replay a bud logical eraseblock.
+ * @c: UBIFS file-system description object
+ * @b: bud entry which describes the bud
+ *
+ * This function replays bud @bud, recovers it if needed, and adds all nodes
+ * from this bud to the replay list. Returns zero in case of success and a
+ * negative error code in case of failure.
+ */
+static int replay_bud(struct ubifs_info *c, struct bud_entry *b)
+{
+ int is_last = is_last_bud(c, b->bud);
+ int err = 0, used = 0, lnum = b->bud->lnum, offs = b->bud->start;
+ struct ubifs_scan_leb *sleb;
+ struct ubifs_scan_node *snod;
+
+ dbg_mnt("replay bud LEB %d, head %d, offs %d, is_last %d",
+ lnum, b->bud->jhead, offs, is_last);
+
+ if (c->need_recovery && is_last)
+ /*
+ * Recover only last LEBs in the journal heads, because power
+ * cuts may cause corruptions only in these LEBs, because only
+ * these LEBs could possibly be written to at the power cut
+ * time.
+ */
+ sleb = ubifs_recover_leb(c, lnum, offs, c->sbuf, b->bud->jhead);
+ else
+ sleb = ubifs_scan(c, lnum, offs, c->sbuf, 0);
+ if (IS_ERR(sleb))
+ return PTR_ERR(sleb);
+
+ /*
+ * The bud does not have to start from offset zero - the beginning of
+ * the 'lnum' LEB may contain previously committed data. One of the
+ * things we have to do in replay is to correctly update lprops with
+ * newer information about this LEB.
+ *
+ * At this point lprops thinks that this LEB has 'c->leb_size - offs'
+ * bytes of free space because it only contain information about
+ * committed data.
+ *
+ * But we know that real amount of free space is 'c->leb_size -
+ * sleb->endpt', and the space in the 'lnum' LEB between 'offs' and
+ * 'sleb->endpt' is used by bud data. We have to correctly calculate
+ * how much of these data are dirty and update lprops with this
+ * information.
+ *
+ * The dirt in that LEB region is comprised of padding nodes, deletion
+ * nodes, truncation nodes and nodes which are obsoleted by subsequent
+ * nodes in this LEB. So instead of calculating clean space, we
+ * calculate used space ('used' variable).
+ */
+
+ list_for_each_entry(snod, &sleb->nodes, list) {
+ int deletion = 0;
+
+ cond_resched();
+
+ if (snod->sqnum >= SQNUM_WATERMARK) {
+ ubifs_err(c, "file system's life ended");
+ goto out_dump;
+ }
+
+ if (snod->sqnum > c->max_sqnum)
+ c->max_sqnum = snod->sqnum;
+
+ switch (snod->type) {
+ case UBIFS_INO_NODE:
+ {
+ struct ubifs_ino_node *ino = snod->node;
+ loff_t new_size = le64_to_cpu(ino->size);
+
+ if (le32_to_cpu(ino->nlink) == 0)
+ deletion = 1;
+ err = insert_node(c, lnum, snod->offs, snod->len,
+ &snod->key, snod->sqnum, deletion,
+ &used, 0, new_size);
+ break;
+ }
+ case UBIFS_DATA_NODE:
+ {
+ struct ubifs_data_node *dn = snod->node;
+ loff_t new_size = le32_to_cpu(dn->size) +
+ key_block(c, &snod->key) *
+ UBIFS_BLOCK_SIZE;
+
+ err = insert_node(c, lnum, snod->offs, snod->len,
+ &snod->key, snod->sqnum, deletion,
+ &used, 0, new_size);
+ break;
+ }
+ case UBIFS_DENT_NODE:
+ case UBIFS_XENT_NODE:
+ {
+ struct ubifs_dent_node *dent = snod->node;
+
+ err = ubifs_validate_entry(c, dent);
+ if (err)
+ goto out_dump;
+
+ err = insert_dent(c, lnum, snod->offs, snod->len,
+ &snod->key, dent->name,
+ le16_to_cpu(dent->nlen), snod->sqnum,
+ !le64_to_cpu(dent->inum), &used);
+ break;
+ }
+ case UBIFS_TRUN_NODE:
+ {
+ struct ubifs_trun_node *trun = snod->node;
+ loff_t old_size = le64_to_cpu(trun->old_size);
+ loff_t new_size = le64_to_cpu(trun->new_size);
+ union ubifs_key key;
+
+ /* Validate truncation node */
+ if (old_size < 0 || old_size > c->max_inode_sz ||
+ new_size < 0 || new_size > c->max_inode_sz ||
+ old_size <= new_size) {
+ ubifs_err(c, "bad truncation node");
+ goto out_dump;
+ }
+
+ /*
+ * Create a fake truncation key just to use the same
+ * functions which expect nodes to have keys.
+ */
+ trun_key_init(c, &key, le32_to_cpu(trun->inum));
+ err = insert_node(c, lnum, snod->offs, snod->len,
+ &key, snod->sqnum, 1, &used,
+ old_size, new_size);
+ break;
+ }
+ default:
+ ubifs_err(c, "unexpected node type %d in bud LEB %d:%d",
+ snod->type, lnum, snod->offs);
+ err = -EINVAL;
+ goto out_dump;
+ }
+ if (err)
+ goto out;
+ }
+
+ ubifs_assert(ubifs_search_bud(c, lnum));
+ ubifs_assert(sleb->endpt - offs >= used);
+ ubifs_assert(sleb->endpt % c->min_io_size == 0);
+
+ b->dirty = sleb->endpt - offs - used;
+ b->free = c->leb_size - sleb->endpt;
+ dbg_mnt("bud LEB %d replied: dirty %d, free %d",
+ lnum, b->dirty, b->free);
+
+out:
+ ubifs_scan_destroy(sleb);
+ return err;
+
+out_dump:
+ ubifs_err(c, "bad node is at LEB %d:%d", lnum, snod->offs);
+ ubifs_dump_node(c, snod->node);
+ ubifs_scan_destroy(sleb);
+ return -EINVAL;
+}
+
+/**
+ * replay_buds - replay all buds.
+ * @c: UBIFS file-system description object
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+static int replay_buds(struct ubifs_info *c)
+{
+ struct bud_entry *b;
+ int err;
+ unsigned long long prev_sqnum = 0;
+
+ list_for_each_entry(b, &c->replay_buds, list) {
+ err = replay_bud(c, b);
+ if (err)
+ return err;
+
+ ubifs_assert(b->sqnum > prev_sqnum);
+ prev_sqnum = b->sqnum;
+ }
+
+ return 0;
+}
+
+/**
+ * destroy_bud_list - destroy the list of buds to replay.
+ * @c: UBIFS file-system description object
+ */
+static void destroy_bud_list(struct ubifs_info *c)
+{
+ struct bud_entry *b;
+
+ while (!list_empty(&c->replay_buds)) {
+ b = list_entry(c->replay_buds.next, struct bud_entry, list);
+ list_del(&b->list);
+ kfree(b);
+ }
+}
+
+/**
+ * add_replay_bud - add a bud to the list of buds to replay.
+ * @c: UBIFS file-system description object
+ * @lnum: bud logical eraseblock number to replay
+ * @offs: bud start offset
+ * @jhead: journal head to which this bud belongs
+ * @sqnum: reference node sequence number
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+static int add_replay_bud(struct ubifs_info *c, int lnum, int offs, int jhead,
+ unsigned long long sqnum)
+{
+ struct ubifs_bud *bud;
+ struct bud_entry *b;
+
+ dbg_mnt("add replay bud LEB %d:%d, head %d", lnum, offs, jhead);
+
+ bud = kmalloc(sizeof(struct ubifs_bud), GFP_KERNEL);
+ if (!bud)
+ return -ENOMEM;
+
+ b = kmalloc(sizeof(struct bud_entry), GFP_KERNEL);
+ if (!b) {
+ kfree(bud);
+ return -ENOMEM;
+ }
+
+ bud->lnum = lnum;
+ bud->start = offs;
+ bud->jhead = jhead;
+ ubifs_add_bud(c, bud);
+
+ b->bud = bud;
+ b->sqnum = sqnum;
+ list_add_tail(&b->list, &c->replay_buds);
+
+ return 0;
+}
+
+/**
+ * validate_ref - validate a reference node.
+ * @c: UBIFS file-system description object
+ * @ref: the reference node to validate
+ * @ref_lnum: LEB number of the reference node
+ * @ref_offs: reference node offset
+ *
+ * This function returns %1 if a bud reference already exists for the LEB. %0 is
+ * returned if the reference node is new, otherwise %-EINVAL is returned if
+ * validation failed.
+ */
+static int validate_ref(struct ubifs_info *c, const struct ubifs_ref_node *ref)
+{
+ struct ubifs_bud *bud;
+ int lnum = le32_to_cpu(ref->lnum);
+ unsigned int offs = le32_to_cpu(ref->offs);
+ unsigned int jhead = le32_to_cpu(ref->jhead);
+
+ /*
+ * ref->offs may point to the end of LEB when the journal head points
+ * to the end of LEB and we write reference node for it during commit.
+ * So this is why we require 'offs > c->leb_size'.
+ */
+ if (jhead >= c->jhead_cnt || lnum >= c->leb_cnt ||
+ lnum < c->main_first || offs > c->leb_size ||
+ offs & (c->min_io_size - 1))
+ return -EINVAL;
+
+ /* Make sure we have not already looked at this bud */
+ bud = ubifs_search_bud(c, lnum);
+ if (bud) {
+ if (bud->jhead == jhead && bud->start <= offs)
+ return 1;
+ ubifs_err(c, "bud at LEB %d:%d was already referred", lnum, offs);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * replay_log_leb - replay a log logical eraseblock.
+ * @c: UBIFS file-system description object
+ * @lnum: log logical eraseblock to replay
+ * @offs: offset to start replaying from
+ * @sbuf: scan buffer
+ *
+ * This function replays a log LEB and returns zero in case of success, %1 if
+ * this is the last LEB in the log, and a negative error code in case of
+ * failure.
+ */
+static int replay_log_leb(struct ubifs_info *c, int lnum, int offs, void *sbuf)
+{
+ int err;
+ struct ubifs_scan_leb *sleb;
+ struct ubifs_scan_node *snod;
+ const struct ubifs_cs_node *node;
+
+ dbg_mnt("replay log LEB %d:%d", lnum, offs);
+ sleb = ubifs_scan(c, lnum, offs, sbuf, c->need_recovery);
+ if (IS_ERR(sleb)) {
+ if (PTR_ERR(sleb) != -EUCLEAN || !c->need_recovery)
+ return PTR_ERR(sleb);
+ /*
+ * Note, the below function will recover this log LEB only if
+ * it is the last, because unclean reboots can possibly corrupt
+ * only the tail of the log.
+ */
+ sleb = ubifs_recover_log_leb(c, lnum, offs, sbuf);
+ if (IS_ERR(sleb))
+ return PTR_ERR(sleb);
+ }
+
+ if (sleb->nodes_cnt == 0) {
+ err = 1;
+ goto out;
+ }
+
+ node = sleb->buf;
+ snod = list_entry(sleb->nodes.next, struct ubifs_scan_node, list);
+ if (c->cs_sqnum == 0) {
+ /*
+ * This is the first log LEB we are looking at, make sure that
+ * the first node is a commit start node. Also record its
+ * sequence number so that UBIFS can determine where the log
+ * ends, because all nodes which were have higher sequence
+ * numbers.
+ */
+ if (snod->type != UBIFS_CS_NODE) {
+ ubifs_err(c, "first log node at LEB %d:%d is not CS node",
+ lnum, offs);
+ goto out_dump;
+ }
+ if (le64_to_cpu(node->cmt_no) != c->cmt_no) {
+ ubifs_err(c, "first CS node at LEB %d:%d has wrong commit number %llu expected %llu",
+ lnum, offs,
+ (unsigned long long)le64_to_cpu(node->cmt_no),
+ c->cmt_no);
+ goto out_dump;
+ }
+
+ c->cs_sqnum = le64_to_cpu(node->ch.sqnum);
+ dbg_mnt("commit start sqnum %llu", c->cs_sqnum);
+ }
+
+ if (snod->sqnum < c->cs_sqnum) {
+ /*
+ * This means that we reached end of log and now
+ * look to the older log data, which was already
+ * committed but the eraseblock was not erased (UBIFS
+ * only un-maps it). So this basically means we have to
+ * exit with "end of log" code.
+ */
+ err = 1;
+ goto out;
+ }
+
+ /* Make sure the first node sits at offset zero of the LEB */
+ if (snod->offs != 0) {
+ ubifs_err(c, "first node is not at zero offset");
+ goto out_dump;
+ }
+
+ list_for_each_entry(snod, &sleb->nodes, list) {
+ cond_resched();
+
+ if (snod->sqnum >= SQNUM_WATERMARK) {
+ ubifs_err(c, "file system's life ended");
+ goto out_dump;
+ }
+
+ if (snod->sqnum < c->cs_sqnum) {
+ ubifs_err(c, "bad sqnum %llu, commit sqnum %llu",
+ snod->sqnum, c->cs_sqnum);
+ goto out_dump;
+ }
+
+ if (snod->sqnum > c->max_sqnum)
+ c->max_sqnum = snod->sqnum;
+
+ switch (snod->type) {
+ case UBIFS_REF_NODE: {
+ const struct ubifs_ref_node *ref = snod->node;
+
+ err = validate_ref(c, ref);
+ if (err == 1)
+ break; /* Already have this bud */
+ if (err)
+ goto out_dump;
+
+ err = add_replay_bud(c, le32_to_cpu(ref->lnum),
+ le32_to_cpu(ref->offs),
+ le32_to_cpu(ref->jhead),
+ snod->sqnum);
+ if (err)
+ goto out;
+
+ break;
+ }
+ case UBIFS_CS_NODE:
+ /* Make sure it sits at the beginning of LEB */
+ if (snod->offs != 0) {
+ ubifs_err(c, "unexpected node in log");
+ goto out_dump;
+ }
+ break;
+ default:
+ ubifs_err(c, "unexpected node in log");
+ goto out_dump;
+ }
+ }
+
+ if (sleb->endpt || c->lhead_offs >= c->leb_size) {
+ c->lhead_lnum = lnum;
+ c->lhead_offs = sleb->endpt;
+ }
+
+ err = !sleb->endpt;
+out:
+ ubifs_scan_destroy(sleb);
+ return err;
+
+out_dump:
+ ubifs_err(c, "log error detected while replaying the log at LEB %d:%d",
+ lnum, offs + snod->offs);
+ ubifs_dump_node(c, snod->node);
+ ubifs_scan_destroy(sleb);
+ return -EINVAL;
+}
+
+/**
+ * take_ihead - update the status of the index head in lprops to 'taken'.
+ * @c: UBIFS file-system description object
+ *
+ * This function returns the amount of free space in the index head LEB or a
+ * negative error code.
+ */
+static int take_ihead(struct ubifs_info *c)
+{
+ const struct ubifs_lprops *lp;
+ int err, free;
+
+ ubifs_get_lprops(c);
+
+ lp = ubifs_lpt_lookup_dirty(c, c->ihead_lnum);
+ if (IS_ERR(lp)) {
+ err = PTR_ERR(lp);
+ goto out;
+ }
+
+ free = lp->free;
+
+ lp = ubifs_change_lp(c, lp, LPROPS_NC, LPROPS_NC,
+ lp->flags | LPROPS_TAKEN, 0);
+ if (IS_ERR(lp)) {
+ err = PTR_ERR(lp);
+ goto out;
+ }
+
+ err = free;
+out:
+ ubifs_release_lprops(c);
+ return err;
+}
+
+/**
+ * ubifs_replay_journal - replay journal.
+ * @c: UBIFS file-system description object
+ *
+ * This function scans the journal, replays and cleans it up. It makes sure all
+ * memory data structures related to uncommitted journal are built (dirty TNC
+ * tree, tree of buds, modified lprops, etc).
+ */
+int ubifs_replay_journal(struct ubifs_info *c)
+{
+ int err, lnum, free;
+
+ BUILD_BUG_ON(UBIFS_TRUN_KEY > 5);
+
+ /* Update the status of the index head in lprops to 'taken' */
+ free = take_ihead(c);
+ if (free < 0)
+ return free; /* Error code */
+
+ if (c->ihead_offs != c->leb_size - free) {
+ ubifs_err(c, "bad index head LEB %d:%d", c->ihead_lnum,
+ c->ihead_offs);
+ return -EINVAL;
+ }
+
+ dbg_mnt("start replaying the journal");
+ c->replaying = 1;
+ lnum = c->ltail_lnum = c->lhead_lnum;
+
+ do {
+ err = replay_log_leb(c, lnum, 0, c->sbuf);
+ if (err == 1) {
+ if (lnum != c->lhead_lnum)
+ /* We hit the end of the log */
+ break;
+
+ /*
+ * The head of the log must always start with the
+ * "commit start" node on a properly formatted UBIFS.
+ * But we found no nodes at all, which means that
+ * someting went wrong and we cannot proceed mounting
+ * the file-system.
+ */
+ ubifs_err(c, "no UBIFS nodes found at the log head LEB %d:%d, possibly corrupted",
+ lnum, 0);
+ err = -EINVAL;
+ }
+ if (err)
+ goto out;
+ lnum = ubifs_next_log_lnum(c, lnum);
+ } while (lnum != c->ltail_lnum);
+
+ err = replay_buds(c);
+ if (err)
+ goto out;
+
+ err = apply_replay_list(c);
+ if (err)
+ goto out;
+
+ err = set_buds_lprops(c);
+ if (err)
+ goto out;
+
+ /*
+ * UBIFS budgeting calculations use @c->bi.uncommitted_idx variable
+ * to roughly estimate index growth. Things like @c->bi.min_idx_lebs
+ * depend on it. This means we have to initialize it to make sure
+ * budgeting works properly.
+ */
+ c->bi.uncommitted_idx = atomic_long_read(&c->dirty_zn_cnt);
+ c->bi.uncommitted_idx *= c->max_idx_node_sz;
+
+ ubifs_assert(c->bud_bytes <= c->max_bud_bytes || c->need_recovery);
+ dbg_mnt("finished, log head LEB %d:%d, max_sqnum %llu, highest_inum %lu",
+ c->lhead_lnum, c->lhead_offs, c->max_sqnum,
+ (unsigned long)c->highest_inum);
+out:
+ destroy_replay_list(c);
+ destroy_bud_list(c);
+ c->replaying = 0;
+ return err;
+}
diff --git a/roms/u-boot/fs/ubifs/sb.c b/roms/u-boot/fs/ubifs/sb.c
new file mode 100644
index 000000000..1d49285c7
--- /dev/null
+++ b/roms/u-boot/fs/ubifs/sb.c
@@ -0,0 +1,821 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ *
+ * Authors: Artem Bityutskiy (Битюцкий Артём)
+ * Adrian Hunter
+ */
+
+/*
+ * This file implements UBIFS superblock. The superblock is stored at the first
+ * LEB of the volume and is never changed by UBIFS. Only user-space tools may
+ * change it. The superblock node mostly contains geometry information.
+ */
+
+#include "ubifs.h"
+#ifndef __UBOOT__
+#include <log.h>
+#include <dm/devres.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/math64.h>
+#else
+
+#include <linux/compat.h>
+#include <linux/err.h>
+#include <ubi_uboot.h>
+#include <linux/stat.h>
+#endif
+
+/*
+ * Default journal size in logical eraseblocks as a percent of total
+ * flash size.
+ */
+#define DEFAULT_JNL_PERCENT 5
+
+/* Default maximum journal size in bytes */
+#define DEFAULT_MAX_JNL (32*1024*1024)
+
+/* Default indexing tree fanout */
+#define DEFAULT_FANOUT 8
+
+/* Default number of data journal heads */
+#define DEFAULT_JHEADS_CNT 1
+
+/* Default positions of different LEBs in the main area */
+#define DEFAULT_IDX_LEB 0
+#define DEFAULT_DATA_LEB 1
+#define DEFAULT_GC_LEB 2
+
+/* Default number of LEB numbers in LPT's save table */
+#define DEFAULT_LSAVE_CNT 256
+
+/* Default reserved pool size as a percent of maximum free space */
+#define DEFAULT_RP_PERCENT 5
+
+/* The default maximum size of reserved pool in bytes */
+#define DEFAULT_MAX_RP_SIZE (5*1024*1024)
+
+/* Default time granularity in nanoseconds */
+#define DEFAULT_TIME_GRAN 1000000000
+
+#ifndef __UBOOT__
+/**
+ * create_default_filesystem - format empty UBI volume.
+ * @c: UBIFS file-system description object
+ *
+ * This function creates default empty file-system. Returns zero in case of
+ * success and a negative error code in case of failure.
+ */
+static int create_default_filesystem(struct ubifs_info *c)
+{
+ struct ubifs_sb_node *sup;
+ struct ubifs_mst_node *mst;
+ struct ubifs_idx_node *idx;
+ struct ubifs_branch *br;
+ struct ubifs_ino_node *ino;
+ struct ubifs_cs_node *cs;
+ union ubifs_key key;
+ int err, tmp, jnl_lebs, log_lebs, max_buds, main_lebs, main_first;
+ int lpt_lebs, lpt_first, orph_lebs, big_lpt, ino_waste, sup_flags = 0;
+ int min_leb_cnt = UBIFS_MIN_LEB_CNT;
+ long long tmp64, main_bytes;
+ __le64 tmp_le64;
+
+ /* Some functions called from here depend on the @c->key_len filed */
+ c->key_len = UBIFS_SK_LEN;
+
+ /*
+ * First of all, we have to calculate default file-system geometry -
+ * log size, journal size, etc.
+ */
+ if (c->leb_cnt < 0x7FFFFFFF / DEFAULT_JNL_PERCENT)
+ /* We can first multiply then divide and have no overflow */
+ jnl_lebs = c->leb_cnt * DEFAULT_JNL_PERCENT / 100;
+ else
+ jnl_lebs = (c->leb_cnt / 100) * DEFAULT_JNL_PERCENT;
+
+ if (jnl_lebs < UBIFS_MIN_JNL_LEBS)
+ jnl_lebs = UBIFS_MIN_JNL_LEBS;
+ if (jnl_lebs * c->leb_size > DEFAULT_MAX_JNL)
+ jnl_lebs = DEFAULT_MAX_JNL / c->leb_size;
+
+ /*
+ * The log should be large enough to fit reference nodes for all bud
+ * LEBs. Because buds do not have to start from the beginning of LEBs
+ * (half of the LEB may contain committed data), the log should
+ * generally be larger, make it twice as large.
+ */
+ tmp = 2 * (c->ref_node_alsz * jnl_lebs) + c->leb_size - 1;
+ log_lebs = tmp / c->leb_size;
+ /* Plus one LEB reserved for commit */
+ log_lebs += 1;
+ if (c->leb_cnt - min_leb_cnt > 8) {
+ /* And some extra space to allow writes while committing */
+ log_lebs += 1;
+ min_leb_cnt += 1;
+ }
+
+ max_buds = jnl_lebs - log_lebs;
+ if (max_buds < UBIFS_MIN_BUD_LEBS)
+ max_buds = UBIFS_MIN_BUD_LEBS;
+
+ /*
+ * Orphan nodes are stored in a separate area. One node can store a lot
+ * of orphan inode numbers, but when new orphan comes we just add a new
+ * orphan node. At some point the nodes are consolidated into one
+ * orphan node.
+ */
+ orph_lebs = UBIFS_MIN_ORPH_LEBS;
+ if (c->leb_cnt - min_leb_cnt > 1)
+ /*
+ * For debugging purposes it is better to have at least 2
+ * orphan LEBs, because the orphan subsystem would need to do
+ * consolidations and would be stressed more.
+ */
+ orph_lebs += 1;
+
+ main_lebs = c->leb_cnt - UBIFS_SB_LEBS - UBIFS_MST_LEBS - log_lebs;
+ main_lebs -= orph_lebs;
+
+ lpt_first = UBIFS_LOG_LNUM + log_lebs;
+ c->lsave_cnt = DEFAULT_LSAVE_CNT;
+ c->max_leb_cnt = c->leb_cnt;
+ err = ubifs_create_dflt_lpt(c, &main_lebs, lpt_first, &lpt_lebs,
+ &big_lpt);
+ if (err)
+ return err;
+
+ dbg_gen("LEB Properties Tree created (LEBs %d-%d)", lpt_first,
+ lpt_first + lpt_lebs - 1);
+
+ main_first = c->leb_cnt - main_lebs;
+
+ /* Create default superblock */
+ tmp = ALIGN(UBIFS_SB_NODE_SZ, c->min_io_size);
+ sup = kzalloc(tmp, GFP_KERNEL);
+ if (!sup)
+ return -ENOMEM;
+
+ tmp64 = (long long)max_buds * c->leb_size;
+ if (big_lpt)
+ sup_flags |= UBIFS_FLG_BIGLPT;
+
+ sup->ch.node_type = UBIFS_SB_NODE;
+ sup->key_hash = UBIFS_KEY_HASH_R5;
+ sup->flags = cpu_to_le32(sup_flags);
+ sup->min_io_size = cpu_to_le32(c->min_io_size);
+ sup->leb_size = cpu_to_le32(c->leb_size);
+ sup->leb_cnt = cpu_to_le32(c->leb_cnt);
+ sup->max_leb_cnt = cpu_to_le32(c->max_leb_cnt);
+ sup->max_bud_bytes = cpu_to_le64(tmp64);
+ sup->log_lebs = cpu_to_le32(log_lebs);
+ sup->lpt_lebs = cpu_to_le32(lpt_lebs);
+ sup->orph_lebs = cpu_to_le32(orph_lebs);
+ sup->jhead_cnt = cpu_to_le32(DEFAULT_JHEADS_CNT);
+ sup->fanout = cpu_to_le32(DEFAULT_FANOUT);
+ sup->lsave_cnt = cpu_to_le32(c->lsave_cnt);
+ sup->fmt_version = cpu_to_le32(UBIFS_FORMAT_VERSION);
+ sup->time_gran = cpu_to_le32(DEFAULT_TIME_GRAN);
+ if (c->mount_opts.override_compr)
+ sup->default_compr = cpu_to_le16(c->mount_opts.compr_type);
+ else
+ sup->default_compr = cpu_to_le16(UBIFS_COMPR_LZO);
+
+ generate_random_uuid(sup->uuid);
+
+ main_bytes = (long long)main_lebs * c->leb_size;
+ tmp64 = div_u64(main_bytes * DEFAULT_RP_PERCENT, 100);
+ if (tmp64 > DEFAULT_MAX_RP_SIZE)
+ tmp64 = DEFAULT_MAX_RP_SIZE;
+ sup->rp_size = cpu_to_le64(tmp64);
+ sup->ro_compat_version = cpu_to_le32(UBIFS_RO_COMPAT_VERSION);
+
+ err = ubifs_write_node(c, sup, UBIFS_SB_NODE_SZ, 0, 0);
+ kfree(sup);
+ if (err)
+ return err;
+
+ dbg_gen("default superblock created at LEB 0:0");
+
+ /* Create default master node */
+ mst = kzalloc(c->mst_node_alsz, GFP_KERNEL);
+ if (!mst)
+ return -ENOMEM;
+
+ mst->ch.node_type = UBIFS_MST_NODE;
+ mst->log_lnum = cpu_to_le32(UBIFS_LOG_LNUM);
+ mst->highest_inum = cpu_to_le64(UBIFS_FIRST_INO);
+ mst->cmt_no = 0;
+ mst->root_lnum = cpu_to_le32(main_first + DEFAULT_IDX_LEB);
+ mst->root_offs = 0;
+ tmp = ubifs_idx_node_sz(c, 1);
+ mst->root_len = cpu_to_le32(tmp);
+ mst->gc_lnum = cpu_to_le32(main_first + DEFAULT_GC_LEB);
+ mst->ihead_lnum = cpu_to_le32(main_first + DEFAULT_IDX_LEB);
+ mst->ihead_offs = cpu_to_le32(ALIGN(tmp, c->min_io_size));
+ mst->index_size = cpu_to_le64(ALIGN(tmp, 8));
+ mst->lpt_lnum = cpu_to_le32(c->lpt_lnum);
+ mst->lpt_offs = cpu_to_le32(c->lpt_offs);
+ mst->nhead_lnum = cpu_to_le32(c->nhead_lnum);
+ mst->nhead_offs = cpu_to_le32(c->nhead_offs);
+ mst->ltab_lnum = cpu_to_le32(c->ltab_lnum);
+ mst->ltab_offs = cpu_to_le32(c->ltab_offs);
+ mst->lsave_lnum = cpu_to_le32(c->lsave_lnum);
+ mst->lsave_offs = cpu_to_le32(c->lsave_offs);
+ mst->lscan_lnum = cpu_to_le32(main_first);
+ mst->empty_lebs = cpu_to_le32(main_lebs - 2);
+ mst->idx_lebs = cpu_to_le32(1);
+ mst->leb_cnt = cpu_to_le32(c->leb_cnt);
+
+ /* Calculate lprops statistics */
+ tmp64 = main_bytes;
+ tmp64 -= ALIGN(ubifs_idx_node_sz(c, 1), c->min_io_size);
+ tmp64 -= ALIGN(UBIFS_INO_NODE_SZ, c->min_io_size);
+ mst->total_free = cpu_to_le64(tmp64);
+
+ tmp64 = ALIGN(ubifs_idx_node_sz(c, 1), c->min_io_size);
+ ino_waste = ALIGN(UBIFS_INO_NODE_SZ, c->min_io_size) -
+ UBIFS_INO_NODE_SZ;
+ tmp64 += ino_waste;
+ tmp64 -= ALIGN(ubifs_idx_node_sz(c, 1), 8);
+ mst->total_dirty = cpu_to_le64(tmp64);
+
+ /* The indexing LEB does not contribute to dark space */
+ tmp64 = ((long long)(c->main_lebs - 1) * c->dark_wm);
+ mst->total_dark = cpu_to_le64(tmp64);
+
+ mst->total_used = cpu_to_le64(UBIFS_INO_NODE_SZ);
+
+ err = ubifs_write_node(c, mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM, 0);
+ if (err) {
+ kfree(mst);
+ return err;
+ }
+ err = ubifs_write_node(c, mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM + 1,
+ 0);
+ kfree(mst);
+ if (err)
+ return err;
+
+ dbg_gen("default master node created at LEB %d:0", UBIFS_MST_LNUM);
+
+ /* Create the root indexing node */
+ tmp = ubifs_idx_node_sz(c, 1);
+ idx = kzalloc(ALIGN(tmp, c->min_io_size), GFP_KERNEL);
+ if (!idx)
+ return -ENOMEM;
+
+ c->key_fmt = UBIFS_SIMPLE_KEY_FMT;
+ c->key_hash = key_r5_hash;
+
+ idx->ch.node_type = UBIFS_IDX_NODE;
+ idx->child_cnt = cpu_to_le16(1);
+ ino_key_init(c, &key, UBIFS_ROOT_INO);
+ br = ubifs_idx_branch(c, idx, 0);
+ key_write_idx(c, &key, &br->key);
+ br->lnum = cpu_to_le32(main_first + DEFAULT_DATA_LEB);
+ br->len = cpu_to_le32(UBIFS_INO_NODE_SZ);
+ err = ubifs_write_node(c, idx, tmp, main_first + DEFAULT_IDX_LEB, 0);
+ kfree(idx);
+ if (err)
+ return err;
+
+ dbg_gen("default root indexing node created LEB %d:0",
+ main_first + DEFAULT_IDX_LEB);
+
+ /* Create default root inode */
+ tmp = ALIGN(UBIFS_INO_NODE_SZ, c->min_io_size);
+ ino = kzalloc(tmp, GFP_KERNEL);
+ if (!ino)
+ return -ENOMEM;
+
+ ino_key_init_flash(c, &ino->key, UBIFS_ROOT_INO);
+ ino->ch.node_type = UBIFS_INO_NODE;
+ ino->creat_sqnum = cpu_to_le64(++c->max_sqnum);
+ ino->nlink = cpu_to_le32(2);
+ tmp_le64 = cpu_to_le64(CURRENT_TIME_SEC.tv_sec);
+ ino->atime_sec = tmp_le64;
+ ino->ctime_sec = tmp_le64;
+ ino->mtime_sec = tmp_le64;
+ ino->atime_nsec = 0;
+ ino->ctime_nsec = 0;
+ ino->mtime_nsec = 0;
+ ino->mode = cpu_to_le32(S_IFDIR | S_IRUGO | S_IWUSR | S_IXUGO);
+ ino->size = cpu_to_le64(UBIFS_INO_NODE_SZ);
+
+ /* Set compression enabled by default */
+ ino->flags = cpu_to_le32(UBIFS_COMPR_FL);
+
+ err = ubifs_write_node(c, ino, UBIFS_INO_NODE_SZ,
+ main_first + DEFAULT_DATA_LEB, 0);
+ kfree(ino);
+ if (err)
+ return err;
+
+ dbg_gen("root inode created at LEB %d:0",
+ main_first + DEFAULT_DATA_LEB);
+
+ /*
+ * The first node in the log has to be the commit start node. This is
+ * always the case during normal file-system operation. Write a fake
+ * commit start node to the log.
+ */
+ tmp = ALIGN(UBIFS_CS_NODE_SZ, c->min_io_size);
+ cs = kzalloc(tmp, GFP_KERNEL);
+ if (!cs)
+ return -ENOMEM;
+
+ cs->ch.node_type = UBIFS_CS_NODE;
+ err = ubifs_write_node(c, cs, UBIFS_CS_NODE_SZ, UBIFS_LOG_LNUM, 0);
+ kfree(cs);
+ if (err)
+ return err;
+
+ ubifs_msg(c, "default file-system created");
+ return 0;
+}
+#endif
+
+/**
+ * validate_sb - validate superblock node.
+ * @c: UBIFS file-system description object
+ * @sup: superblock node
+ *
+ * This function validates superblock node @sup. Since most of data was read
+ * from the superblock and stored in @c, the function validates fields in @c
+ * instead. Returns zero in case of success and %-EINVAL in case of validation
+ * failure.
+ */
+static int validate_sb(struct ubifs_info *c, struct ubifs_sb_node *sup)
+{
+ long long max_bytes;
+ int err = 1, min_leb_cnt;
+
+ if (!c->key_hash) {
+ err = 2;
+ goto failed;
+ }
+
+ if (sup->key_fmt != UBIFS_SIMPLE_KEY_FMT) {
+ err = 3;
+ goto failed;
+ }
+
+ if (le32_to_cpu(sup->min_io_size) != c->min_io_size) {
+ ubifs_err(c, "min. I/O unit mismatch: %d in superblock, %d real",
+ le32_to_cpu(sup->min_io_size), c->min_io_size);
+ goto failed;
+ }
+
+ if (le32_to_cpu(sup->leb_size) != c->leb_size) {
+ ubifs_err(c, "LEB size mismatch: %d in superblock, %d real",
+ le32_to_cpu(sup->leb_size), c->leb_size);
+ goto failed;
+ }
+
+ if (c->log_lebs < UBIFS_MIN_LOG_LEBS ||
+ c->lpt_lebs < UBIFS_MIN_LPT_LEBS ||
+ c->orph_lebs < UBIFS_MIN_ORPH_LEBS ||
+ c->main_lebs < UBIFS_MIN_MAIN_LEBS) {
+ err = 4;
+ goto failed;
+ }
+
+ /*
+ * Calculate minimum allowed amount of main area LEBs. This is very
+ * similar to %UBIFS_MIN_LEB_CNT, but we take into account real what we
+ * have just read from the superblock.
+ */
+ min_leb_cnt = UBIFS_SB_LEBS + UBIFS_MST_LEBS + c->log_lebs;
+ min_leb_cnt += c->lpt_lebs + c->orph_lebs + c->jhead_cnt + 6;
+
+ if (c->leb_cnt < min_leb_cnt || c->leb_cnt > c->vi.size) {
+ ubifs_err(c, "bad LEB count: %d in superblock, %d on UBI volume, %d minimum required",
+ c->leb_cnt, c->vi.size, min_leb_cnt);
+ goto failed;
+ }
+
+ if (c->max_leb_cnt < c->leb_cnt) {
+ ubifs_err(c, "max. LEB count %d less than LEB count %d",
+ c->max_leb_cnt, c->leb_cnt);
+ goto failed;
+ }
+
+ if (c->main_lebs < UBIFS_MIN_MAIN_LEBS) {
+ ubifs_err(c, "too few main LEBs count %d, must be at least %d",
+ c->main_lebs, UBIFS_MIN_MAIN_LEBS);
+ goto failed;
+ }
+
+ max_bytes = (long long)c->leb_size * UBIFS_MIN_BUD_LEBS;
+ if (c->max_bud_bytes < max_bytes) {
+ ubifs_err(c, "too small journal (%lld bytes), must be at least %lld bytes",
+ c->max_bud_bytes, max_bytes);
+ goto failed;
+ }
+
+ max_bytes = (long long)c->leb_size * c->main_lebs;
+ if (c->max_bud_bytes > max_bytes) {
+ ubifs_err(c, "too large journal size (%lld bytes), only %lld bytes available in the main area",
+ c->max_bud_bytes, max_bytes);
+ goto failed;
+ }
+
+ if (c->jhead_cnt < NONDATA_JHEADS_CNT + 1 ||
+ c->jhead_cnt > NONDATA_JHEADS_CNT + UBIFS_MAX_JHEADS) {
+ err = 9;
+ goto failed;
+ }
+
+ if (c->fanout < UBIFS_MIN_FANOUT ||
+ ubifs_idx_node_sz(c, c->fanout) > c->leb_size) {
+ err = 10;
+ goto failed;
+ }
+
+ if (c->lsave_cnt < 0 || (c->lsave_cnt > DEFAULT_LSAVE_CNT &&
+ c->lsave_cnt > c->max_leb_cnt - UBIFS_SB_LEBS - UBIFS_MST_LEBS -
+ c->log_lebs - c->lpt_lebs - c->orph_lebs)) {
+ err = 11;
+ goto failed;
+ }
+
+ if (UBIFS_SB_LEBS + UBIFS_MST_LEBS + c->log_lebs + c->lpt_lebs +
+ c->orph_lebs + c->main_lebs != c->leb_cnt) {
+ err = 12;
+ goto failed;
+ }
+
+ if (c->default_compr >= UBIFS_COMPR_TYPES_CNT) {
+ err = 13;
+ goto failed;
+ }
+
+ if (c->rp_size < 0 || max_bytes < c->rp_size) {
+ err = 14;
+ goto failed;
+ }
+
+ if (le32_to_cpu(sup->time_gran) > 1000000000 ||
+ le32_to_cpu(sup->time_gran) < 1) {
+ err = 15;
+ goto failed;
+ }
+
+ return 0;
+
+failed:
+ ubifs_err(c, "bad superblock, error %d", err);
+ ubifs_dump_node(c, sup);
+ return -EINVAL;
+}
+
+/**
+ * ubifs_read_sb_node - read superblock node.
+ * @c: UBIFS file-system description object
+ *
+ * This function returns a pointer to the superblock node or a negative error
+ * code. Note, the user of this function is responsible of kfree()'ing the
+ * returned superblock buffer.
+ */
+struct ubifs_sb_node *ubifs_read_sb_node(struct ubifs_info *c)
+{
+ struct ubifs_sb_node *sup;
+ int err;
+
+ sup = kmalloc(ALIGN(UBIFS_SB_NODE_SZ, c->min_io_size), GFP_NOFS);
+ if (!sup)
+ return ERR_PTR(-ENOMEM);
+
+ err = ubifs_read_node(c, sup, UBIFS_SB_NODE, UBIFS_SB_NODE_SZ,
+ UBIFS_SB_LNUM, 0);
+ if (err) {
+ kfree(sup);
+ return ERR_PTR(err);
+ }
+
+ return sup;
+}
+
+/**
+ * ubifs_write_sb_node - write superblock node.
+ * @c: UBIFS file-system description object
+ * @sup: superblock node read with 'ubifs_read_sb_node()'
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int ubifs_write_sb_node(struct ubifs_info *c, struct ubifs_sb_node *sup)
+{
+ int len = ALIGN(UBIFS_SB_NODE_SZ, c->min_io_size);
+
+ ubifs_prepare_node(c, sup, UBIFS_SB_NODE_SZ, 1);
+ return ubifs_leb_change(c, UBIFS_SB_LNUM, sup, len);
+}
+
+/**
+ * ubifs_read_superblock - read superblock.
+ * @c: UBIFS file-system description object
+ *
+ * This function finds, reads and checks the superblock. If an empty UBI volume
+ * is being mounted, this function creates default superblock. Returns zero in
+ * case of success, and a negative error code in case of failure.
+ */
+int ubifs_read_superblock(struct ubifs_info *c)
+{
+ int err, sup_flags;
+ struct ubifs_sb_node *sup;
+
+ if (c->empty) {
+#ifndef __UBOOT__
+ err = create_default_filesystem(c);
+ if (err)
+ return err;
+#else
+ printf("No UBIFS filesystem found!\n");
+ return -1;
+#endif
+ }
+
+ sup = ubifs_read_sb_node(c);
+ if (IS_ERR(sup))
+ return PTR_ERR(sup);
+
+ c->fmt_version = le32_to_cpu(sup->fmt_version);
+ c->ro_compat_version = le32_to_cpu(sup->ro_compat_version);
+
+ /*
+ * The software supports all previous versions but not future versions,
+ * due to the unavailability of time-travelling equipment.
+ */
+ if (c->fmt_version > UBIFS_FORMAT_VERSION) {
+ ubifs_assert(!c->ro_media || c->ro_mount);
+ if (!c->ro_mount ||
+ c->ro_compat_version > UBIFS_RO_COMPAT_VERSION) {
+ ubifs_err(c, "on-flash format version is w%d/r%d, but software only supports up to version w%d/r%d",
+ c->fmt_version, c->ro_compat_version,
+ UBIFS_FORMAT_VERSION,
+ UBIFS_RO_COMPAT_VERSION);
+ if (c->ro_compat_version <= UBIFS_RO_COMPAT_VERSION) {
+ ubifs_msg(c, "only R/O mounting is possible");
+ err = -EROFS;
+ } else
+ err = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * The FS is mounted R/O, and the media format is
+ * R/O-compatible with the UBIFS implementation, so we can
+ * mount.
+ */
+ c->rw_incompat = 1;
+ }
+
+ if (c->fmt_version < 3) {
+ ubifs_err(c, "on-flash format version %d is not supported",
+ c->fmt_version);
+ err = -EINVAL;
+ goto out;
+ }
+
+ switch (sup->key_hash) {
+ case UBIFS_KEY_HASH_R5:
+ c->key_hash = key_r5_hash;
+ c->key_hash_type = UBIFS_KEY_HASH_R5;
+ break;
+
+ case UBIFS_KEY_HASH_TEST:
+ c->key_hash = key_test_hash;
+ c->key_hash_type = UBIFS_KEY_HASH_TEST;
+ break;
+ };
+
+ c->key_fmt = sup->key_fmt;
+
+ switch (c->key_fmt) {
+ case UBIFS_SIMPLE_KEY_FMT:
+ c->key_len = UBIFS_SK_LEN;
+ break;
+ default:
+ ubifs_err(c, "unsupported key format");
+ err = -EINVAL;
+ goto out;
+ }
+
+ c->leb_cnt = le32_to_cpu(sup->leb_cnt);
+ c->max_leb_cnt = le32_to_cpu(sup->max_leb_cnt);
+ c->max_bud_bytes = le64_to_cpu(sup->max_bud_bytes);
+ c->log_lebs = le32_to_cpu(sup->log_lebs);
+ c->lpt_lebs = le32_to_cpu(sup->lpt_lebs);
+ c->orph_lebs = le32_to_cpu(sup->orph_lebs);
+ c->jhead_cnt = le32_to_cpu(sup->jhead_cnt) + NONDATA_JHEADS_CNT;
+ c->fanout = le32_to_cpu(sup->fanout);
+ c->lsave_cnt = le32_to_cpu(sup->lsave_cnt);
+ c->rp_size = le64_to_cpu(sup->rp_size);
+#ifndef __UBOOT__
+ c->rp_uid = make_kuid(&init_user_ns, le32_to_cpu(sup->rp_uid));
+ c->rp_gid = make_kgid(&init_user_ns, le32_to_cpu(sup->rp_gid));
+#else
+ c->rp_uid.val = le32_to_cpu(sup->rp_uid);
+ c->rp_gid.val = le32_to_cpu(sup->rp_gid);
+#endif
+ sup_flags = le32_to_cpu(sup->flags);
+ if (!c->mount_opts.override_compr)
+ c->default_compr = le16_to_cpu(sup->default_compr);
+
+ c->vfs_sb->s_time_gran = le32_to_cpu(sup->time_gran);
+ memcpy(&c->uuid, &sup->uuid, 16);
+ c->big_lpt = !!(sup_flags & UBIFS_FLG_BIGLPT);
+ c->space_fixup = !!(sup_flags & UBIFS_FLG_SPACE_FIXUP);
+
+ /* Automatically increase file system size to the maximum size */
+ c->old_leb_cnt = c->leb_cnt;
+ if (c->leb_cnt < c->vi.size && c->leb_cnt < c->max_leb_cnt) {
+ c->leb_cnt = min_t(int, c->max_leb_cnt, c->vi.size);
+ if (c->ro_mount)
+ dbg_mnt("Auto resizing (ro) from %d LEBs to %d LEBs",
+ c->old_leb_cnt, c->leb_cnt);
+#ifndef __UBOOT__
+ else {
+ dbg_mnt("Auto resizing (sb) from %d LEBs to %d LEBs",
+ c->old_leb_cnt, c->leb_cnt);
+ sup->leb_cnt = cpu_to_le32(c->leb_cnt);
+ err = ubifs_write_sb_node(c, sup);
+ if (err)
+ goto out;
+ c->old_leb_cnt = c->leb_cnt;
+ }
+#endif
+ }
+
+ c->log_bytes = (long long)c->log_lebs * c->leb_size;
+ c->log_last = UBIFS_LOG_LNUM + c->log_lebs - 1;
+ c->lpt_first = UBIFS_LOG_LNUM + c->log_lebs;
+ c->lpt_last = c->lpt_first + c->lpt_lebs - 1;
+ c->orph_first = c->lpt_last + 1;
+ c->orph_last = c->orph_first + c->orph_lebs - 1;
+ c->main_lebs = c->leb_cnt - UBIFS_SB_LEBS - UBIFS_MST_LEBS;
+ c->main_lebs -= c->log_lebs + c->lpt_lebs + c->orph_lebs;
+ c->main_first = c->leb_cnt - c->main_lebs;
+
+ err = validate_sb(c, sup);
+out:
+ kfree(sup);
+ return err;
+}
+
+/**
+ * fixup_leb - fixup/unmap an LEB containing free space.
+ * @c: UBIFS file-system description object
+ * @lnum: the LEB number to fix up
+ * @len: number of used bytes in LEB (starting at offset 0)
+ *
+ * This function reads the contents of the given LEB number @lnum, then fixes
+ * it up, so that empty min. I/O units in the end of LEB are actually erased on
+ * flash (rather than being just all-0xff real data). If the LEB is completely
+ * empty, it is simply unmapped.
+ */
+static int fixup_leb(struct ubifs_info *c, int lnum, int len)
+{
+ int err;
+
+ ubifs_assert(len >= 0);
+ ubifs_assert(len % c->min_io_size == 0);
+ ubifs_assert(len < c->leb_size);
+
+ if (len == 0) {
+ dbg_mnt("unmap empty LEB %d", lnum);
+ return ubifs_leb_unmap(c, lnum);
+ }
+
+ dbg_mnt("fixup LEB %d, data len %d", lnum, len);
+ err = ubifs_leb_read(c, lnum, c->sbuf, 0, len, 1);
+ if (err)
+ return err;
+
+ return ubifs_leb_change(c, lnum, c->sbuf, len);
+}
+
+/**
+ * fixup_free_space - find & remap all LEBs containing free space.
+ * @c: UBIFS file-system description object
+ *
+ * This function walks through all LEBs in the filesystem and fiexes up those
+ * containing free/empty space.
+ */
+static int fixup_free_space(struct ubifs_info *c)
+{
+ int lnum, err = 0;
+ struct ubifs_lprops *lprops;
+
+ ubifs_get_lprops(c);
+
+ /* Fixup LEBs in the master area */
+ for (lnum = UBIFS_MST_LNUM; lnum < UBIFS_LOG_LNUM; lnum++) {
+ err = fixup_leb(c, lnum, c->mst_offs + c->mst_node_alsz);
+ if (err)
+ goto out;
+ }
+
+ /* Unmap unused log LEBs */
+ lnum = ubifs_next_log_lnum(c, c->lhead_lnum);
+ while (lnum != c->ltail_lnum) {
+ err = fixup_leb(c, lnum, 0);
+ if (err)
+ goto out;
+ lnum = ubifs_next_log_lnum(c, lnum);
+ }
+
+ /*
+ * Fixup the log head which contains the only a CS node at the
+ * beginning.
+ */
+ err = fixup_leb(c, c->lhead_lnum,
+ ALIGN(UBIFS_CS_NODE_SZ, c->min_io_size));
+ if (err)
+ goto out;
+
+ /* Fixup LEBs in the LPT area */
+ for (lnum = c->lpt_first; lnum <= c->lpt_last; lnum++) {
+ int free = c->ltab[lnum - c->lpt_first].free;
+
+ if (free > 0) {
+ err = fixup_leb(c, lnum, c->leb_size - free);
+ if (err)
+ goto out;
+ }
+ }
+
+ /* Unmap LEBs in the orphans area */
+ for (lnum = c->orph_first; lnum <= c->orph_last; lnum++) {
+ err = fixup_leb(c, lnum, 0);
+ if (err)
+ goto out;
+ }
+
+ /* Fixup LEBs in the main area */
+ for (lnum = c->main_first; lnum < c->leb_cnt; lnum++) {
+ lprops = ubifs_lpt_lookup(c, lnum);
+ if (IS_ERR(lprops)) {
+ err = PTR_ERR(lprops);
+ goto out;
+ }
+
+ if (lprops->free > 0) {
+ err = fixup_leb(c, lnum, c->leb_size - lprops->free);
+ if (err)
+ goto out;
+ }
+ }
+
+out:
+ ubifs_release_lprops(c);
+ return err;
+}
+
+/**
+ * ubifs_fixup_free_space - find & fix all LEBs with free space.
+ * @c: UBIFS file-system description object
+ *
+ * This function fixes up LEBs containing free space on first mount, if the
+ * appropriate flag was set when the FS was created. Each LEB with one or more
+ * empty min. I/O unit (i.e. free-space-count > 0) is re-written, to make sure
+ * the free space is actually erased. E.g., this is necessary for some NAND
+ * chips, since the free space may have been programmed like real "0xff" data
+ * (generating a non-0xff ECC), causing future writes to the not-really-erased
+ * NAND pages to behave badly. After the space is fixed up, the superblock flag
+ * is cleared, so that this is skipped for all future mounts.
+ */
+int ubifs_fixup_free_space(struct ubifs_info *c)
+{
+ int err;
+ struct ubifs_sb_node *sup;
+
+ ubifs_assert(c->space_fixup);
+ ubifs_assert(!c->ro_mount);
+
+ ubifs_msg(c, "start fixing up free space");
+
+ err = fixup_free_space(c);
+ if (err)
+ return err;
+
+ sup = ubifs_read_sb_node(c);
+ if (IS_ERR(sup))
+ return PTR_ERR(sup);
+
+ /* Free-space fixup is no longer required */
+ c->space_fixup = 0;
+ sup->flags &= cpu_to_le32(~UBIFS_FLG_SPACE_FIXUP);
+
+ err = ubifs_write_sb_node(c, sup);
+ kfree(sup);
+ if (err)
+ return err;
+
+ ubifs_msg(c, "free space fixup complete");
+ return err;
+}
diff --git a/roms/u-boot/fs/ubifs/scan.c b/roms/u-boot/fs/ubifs/scan.c
new file mode 100644
index 000000000..7a6e46df5
--- /dev/null
+++ b/roms/u-boot/fs/ubifs/scan.c
@@ -0,0 +1,373 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation
+ *
+ * Authors: Adrian Hunter
+ * Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * This file implements the scan which is a general-purpose function for
+ * determining what nodes are in an eraseblock. The scan is used to replay the
+ * journal, to do garbage collection. for the TNC in-the-gaps method, and by
+ * debugging functions.
+ */
+
+#ifdef __UBOOT__
+#include <hexdump.h>
+#include <log.h>
+#include <dm/devres.h>
+#include <linux/err.h>
+#endif
+#include "ubifs.h"
+
+/**
+ * scan_padding_bytes - scan for padding bytes.
+ * @buf: buffer to scan
+ * @len: length of buffer
+ *
+ * This function returns the number of padding bytes on success and
+ * %SCANNED_GARBAGE on failure.
+ */
+static int scan_padding_bytes(void *buf, int len)
+{
+ int pad_len = 0, max_pad_len = min_t(int, UBIFS_PAD_NODE_SZ, len);
+ uint8_t *p = buf;
+
+ dbg_scan("not a node");
+
+ while (pad_len < max_pad_len && *p++ == UBIFS_PADDING_BYTE)
+ pad_len += 1;
+
+ if (!pad_len || (pad_len & 7))
+ return SCANNED_GARBAGE;
+
+ dbg_scan("%d padding bytes", pad_len);
+
+ return pad_len;
+}
+
+/**
+ * ubifs_scan_a_node - scan for a node or padding.
+ * @c: UBIFS file-system description object
+ * @buf: buffer to scan
+ * @len: length of buffer
+ * @lnum: logical eraseblock number
+ * @offs: offset within the logical eraseblock
+ * @quiet: print no messages
+ *
+ * This function returns a scanning code to indicate what was scanned.
+ */
+int ubifs_scan_a_node(const struct ubifs_info *c, void *buf, int len, int lnum,
+ int offs, int quiet)
+{
+ struct ubifs_ch *ch = buf;
+ uint32_t magic;
+
+ magic = le32_to_cpu(ch->magic);
+
+ if (magic == 0xFFFFFFFF) {
+ dbg_scan("hit empty space at LEB %d:%d", lnum, offs);
+ return SCANNED_EMPTY_SPACE;
+ }
+
+ if (magic != UBIFS_NODE_MAGIC)
+ return scan_padding_bytes(buf, len);
+
+ if (len < UBIFS_CH_SZ)
+ return SCANNED_GARBAGE;
+
+ dbg_scan("scanning %s at LEB %d:%d",
+ dbg_ntype(ch->node_type), lnum, offs);
+
+ if (ubifs_check_node(c, buf, lnum, offs, quiet, 1))
+ return SCANNED_A_CORRUPT_NODE;
+
+ if (ch->node_type == UBIFS_PAD_NODE) {
+ struct ubifs_pad_node *pad = buf;
+ int pad_len = le32_to_cpu(pad->pad_len);
+ int node_len = le32_to_cpu(ch->len);
+
+ /* Validate the padding node */
+ if (pad_len < 0 ||
+ offs + node_len + pad_len > c->leb_size) {
+ if (!quiet) {
+ ubifs_err(c, "bad pad node at LEB %d:%d",
+ lnum, offs);
+ ubifs_dump_node(c, pad);
+ }
+ return SCANNED_A_BAD_PAD_NODE;
+ }
+
+ /* Make the node pads to 8-byte boundary */
+ if ((node_len + pad_len) & 7) {
+ if (!quiet)
+ ubifs_err(c, "bad padding length %d - %d",
+ offs, offs + node_len + pad_len);
+ return SCANNED_A_BAD_PAD_NODE;
+ }
+
+ dbg_scan("%d bytes padded at LEB %d:%d, offset now %d", pad_len,
+ lnum, offs, ALIGN(offs + node_len + pad_len, 8));
+
+ return node_len + pad_len;
+ }
+
+ return SCANNED_A_NODE;
+}
+
+/**
+ * ubifs_start_scan - create LEB scanning information at start of scan.
+ * @c: UBIFS file-system description object
+ * @lnum: logical eraseblock number
+ * @offs: offset to start at (usually zero)
+ * @sbuf: scan buffer (must be c->leb_size)
+ *
+ * This function returns the scanned information on success and a negative error
+ * code on failure.
+ */
+struct ubifs_scan_leb *ubifs_start_scan(const struct ubifs_info *c, int lnum,
+ int offs, void *sbuf)
+{
+ struct ubifs_scan_leb *sleb;
+ int err;
+
+ dbg_scan("scan LEB %d:%d", lnum, offs);
+
+ sleb = kzalloc(sizeof(struct ubifs_scan_leb), GFP_NOFS);
+ if (!sleb)
+ return ERR_PTR(-ENOMEM);
+
+ sleb->lnum = lnum;
+ INIT_LIST_HEAD(&sleb->nodes);
+ sleb->buf = sbuf;
+
+ err = ubifs_leb_read(c, lnum, sbuf + offs, offs, c->leb_size - offs, 0);
+ if (err && err != -EBADMSG) {
+ ubifs_err(c, "cannot read %d bytes from LEB %d:%d, error %d",
+ c->leb_size - offs, lnum, offs, err);
+ kfree(sleb);
+ return ERR_PTR(err);
+ }
+
+ /*
+ * Note, we ignore integrity errors (EBASMSG) because all the nodes are
+ * protected by CRC checksums.
+ */
+ return sleb;
+}
+
+/**
+ * ubifs_end_scan - update LEB scanning information at end of scan.
+ * @c: UBIFS file-system description object
+ * @sleb: scanning information
+ * @lnum: logical eraseblock number
+ * @offs: offset to start at (usually zero)
+ */
+void ubifs_end_scan(const struct ubifs_info *c, struct ubifs_scan_leb *sleb,
+ int lnum, int offs)
+{
+ lnum = lnum;
+ dbg_scan("stop scanning LEB %d at offset %d", lnum, offs);
+ ubifs_assert(offs % c->min_io_size == 0);
+
+ sleb->endpt = ALIGN(offs, c->min_io_size);
+}
+
+/**
+ * ubifs_add_snod - add a scanned node to LEB scanning information.
+ * @c: UBIFS file-system description object
+ * @sleb: scanning information
+ * @buf: buffer containing node
+ * @offs: offset of node on flash
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int ubifs_add_snod(const struct ubifs_info *c, struct ubifs_scan_leb *sleb,
+ void *buf, int offs)
+{
+ struct ubifs_ch *ch = buf;
+ struct ubifs_ino_node *ino = buf;
+ struct ubifs_scan_node *snod;
+
+ snod = kmalloc(sizeof(struct ubifs_scan_node), GFP_NOFS);
+ if (!snod)
+ return -ENOMEM;
+
+ snod->sqnum = le64_to_cpu(ch->sqnum);
+ snod->type = ch->node_type;
+ snod->offs = offs;
+ snod->len = le32_to_cpu(ch->len);
+ snod->node = buf;
+
+ switch (ch->node_type) {
+ case UBIFS_INO_NODE:
+ case UBIFS_DENT_NODE:
+ case UBIFS_XENT_NODE:
+ case UBIFS_DATA_NODE:
+ /*
+ * The key is in the same place in all keyed
+ * nodes.
+ */
+ key_read(c, &ino->key, &snod->key);
+ break;
+ default:
+ invalid_key_init(c, &snod->key);
+ break;
+ }
+ list_add_tail(&snod->list, &sleb->nodes);
+ sleb->nodes_cnt += 1;
+ return 0;
+}
+
+/**
+ * ubifs_scanned_corruption - print information after UBIFS scanned corruption.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number of corruption
+ * @offs: offset of corruption
+ * @buf: buffer containing corruption
+ */
+void ubifs_scanned_corruption(const struct ubifs_info *c, int lnum, int offs,
+ void *buf)
+{
+ int len;
+
+ ubifs_err(c, "corruption at LEB %d:%d", lnum, offs);
+ len = c->leb_size - offs;
+ if (len > 8192)
+ len = 8192;
+ ubifs_err(c, "first %d bytes from LEB %d:%d", len, lnum, offs);
+ print_hex_dump("", DUMP_PREFIX_OFFSET, 32, 4, buf, len, 1);
+}
+
+/**
+ * ubifs_scan - scan a logical eraseblock.
+ * @c: UBIFS file-system description object
+ * @lnum: logical eraseblock number
+ * @offs: offset to start at (usually zero)
+ * @sbuf: scan buffer (must be of @c->leb_size bytes in size)
+ * @quiet: print no messages
+ *
+ * This function scans LEB number @lnum and returns complete information about
+ * its contents. Returns the scanned information in case of success and,
+ * %-EUCLEAN if the LEB neads recovery, and other negative error codes in case
+ * of failure.
+ *
+ * If @quiet is non-zero, this function does not print large and scary
+ * error messages and flash dumps in case of errors.
+ */
+struct ubifs_scan_leb *ubifs_scan(const struct ubifs_info *c, int lnum,
+ int offs, void *sbuf, int quiet)
+{
+ void *buf = sbuf + offs;
+ int err, len = c->leb_size - offs;
+ struct ubifs_scan_leb *sleb;
+
+ sleb = ubifs_start_scan(c, lnum, offs, sbuf);
+ if (IS_ERR(sleb))
+ return sleb;
+
+ while (len >= 8) {
+ struct ubifs_ch *ch = buf;
+ int node_len, ret;
+
+ dbg_scan("look at LEB %d:%d (%d bytes left)",
+ lnum, offs, len);
+
+ cond_resched();
+
+ ret = ubifs_scan_a_node(c, buf, len, lnum, offs, quiet);
+ if (ret > 0) {
+ /* Padding bytes or a valid padding node */
+ offs += ret;
+ buf += ret;
+ len -= ret;
+ continue;
+ }
+
+ if (ret == SCANNED_EMPTY_SPACE)
+ /* Empty space is checked later */
+ break;
+
+ switch (ret) {
+ case SCANNED_GARBAGE:
+ ubifs_err(c, "garbage");
+ goto corrupted;
+ case SCANNED_A_NODE:
+ break;
+ case SCANNED_A_CORRUPT_NODE:
+ case SCANNED_A_BAD_PAD_NODE:
+ ubifs_err(c, "bad node");
+ goto corrupted;
+ default:
+ ubifs_err(c, "unknown");
+ err = -EINVAL;
+ goto error;
+ }
+
+ err = ubifs_add_snod(c, sleb, buf, offs);
+ if (err)
+ goto error;
+
+ node_len = ALIGN(le32_to_cpu(ch->len), 8);
+ offs += node_len;
+ buf += node_len;
+ len -= node_len;
+ }
+
+ if (offs % c->min_io_size) {
+ if (!quiet)
+ ubifs_err(c, "empty space starts at non-aligned offset %d",
+ offs);
+ goto corrupted;
+ }
+
+ ubifs_end_scan(c, sleb, lnum, offs);
+
+ for (; len > 4; offs += 4, buf = buf + 4, len -= 4)
+ if (*(uint32_t *)buf != 0xffffffff)
+ break;
+ for (; len; offs++, buf++, len--)
+ if (*(uint8_t *)buf != 0xff) {
+ if (!quiet)
+ ubifs_err(c, "corrupt empty space at LEB %d:%d",
+ lnum, offs);
+ goto corrupted;
+ }
+
+ return sleb;
+
+corrupted:
+ if (!quiet) {
+ ubifs_scanned_corruption(c, lnum, offs, buf);
+ ubifs_err(c, "LEB %d scanning failed", lnum);
+ }
+ err = -EUCLEAN;
+ ubifs_scan_destroy(sleb);
+ return ERR_PTR(err);
+
+error:
+ ubifs_err(c, "LEB %d scanning failed, error %d", lnum, err);
+ ubifs_scan_destroy(sleb);
+ return ERR_PTR(err);
+}
+
+/**
+ * ubifs_scan_destroy - destroy LEB scanning information.
+ * @sleb: scanning information to free
+ */
+void ubifs_scan_destroy(struct ubifs_scan_leb *sleb)
+{
+ struct ubifs_scan_node *node;
+ struct list_head *head;
+
+ head = &sleb->nodes;
+ while (!list_empty(head)) {
+ node = list_entry(head->next, struct ubifs_scan_node, list);
+ list_del(&node->list);
+ kfree(node);
+ }
+ kfree(sleb);
+}
diff --git a/roms/u-boot/fs/ubifs/super.c b/roms/u-boot/fs/ubifs/super.c
new file mode 100644
index 000000000..e3a4c0bca
--- /dev/null
+++ b/roms/u-boot/fs/ubifs/super.c
@@ -0,0 +1,2712 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ *
+ * Authors: Artem Bityutskiy (Битюцкий Артём)
+ * Adrian Hunter
+ */
+
+/*
+ * This file implements UBIFS initialization and VFS superblock operations. Some
+ * initialization stuff which is rather large and complex is placed at
+ * corresponding subsystems, but most of it is here.
+ */
+
+#ifndef __UBOOT__
+#include <log.h>
+#include <dm/devres.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/ctype.h>
+#include <linux/kthread.h>
+#include <linux/parser.h>
+#include <linux/seq_file.h>
+#include <linux/mount.h>
+#include <linux/math64.h>
+#include <linux/writeback.h>
+#else
+
+#include <common.h>
+#include <malloc.h>
+#include <memalign.h>
+#include <linux/bitops.h>
+#include <linux/bug.h>
+#include <linux/log2.h>
+#include <linux/stat.h>
+#include <linux/err.h>
+#include "ubifs.h"
+#include <ubi_uboot.h>
+#include <linux/stringify.h>
+#include <mtd/ubi-user.h>
+
+struct dentry;
+struct file;
+struct iattr;
+struct kstat;
+struct vfsmount;
+
+#define INODE_LOCKED_MAX 64
+
+struct super_block *ubifs_sb;
+
+static struct inode *inodes_locked_down[INODE_LOCKED_MAX];
+
+int set_anon_super(struct super_block *s, void *data)
+{
+ return 0;
+}
+
+struct inode *iget_locked(struct super_block *sb, unsigned long ino)
+{
+ struct inode *inode;
+
+ inode = (struct inode *)malloc_cache_aligned(
+ sizeof(struct ubifs_inode));
+ if (inode) {
+ inode->i_ino = ino;
+ inode->i_sb = sb;
+ list_add(&inode->i_sb_list, &sb->s_inodes);
+ inode->i_state = I_LOCK | I_NEW;
+ }
+
+ return inode;
+}
+
+void iget_failed(struct inode *inode)
+{
+}
+
+int ubifs_iput(struct inode *inode)
+{
+ list_del_init(&inode->i_sb_list);
+
+ free(inode);
+ return 0;
+}
+
+/*
+ * Lock (save) inode in inode array for readback after recovery
+ */
+void iput(struct inode *inode)
+{
+ int i;
+ struct inode *ino;
+
+ /*
+ * Search end of list
+ */
+ for (i = 0; i < INODE_LOCKED_MAX; i++) {
+ if (inodes_locked_down[i] == NULL)
+ break;
+ }
+
+ if (i >= INODE_LOCKED_MAX) {
+ dbg_gen("Error, can't lock (save) more inodes while recovery!!!");
+ return;
+ }
+
+ /*
+ * Allocate and use new inode
+ */
+ ino = (struct inode *)malloc_cache_aligned(sizeof(struct ubifs_inode));
+ memcpy(ino, inode, sizeof(struct ubifs_inode));
+
+ /*
+ * Finally save inode in array
+ */
+ inodes_locked_down[i] = ino;
+}
+
+/* from fs/inode.c */
+/**
+ * clear_nlink - directly zero an inode's link count
+ * @inode: inode
+ *
+ * This is a low-level filesystem helper to replace any
+ * direct filesystem manipulation of i_nlink. See
+ * drop_nlink() for why we care about i_nlink hitting zero.
+ */
+void clear_nlink(struct inode *inode)
+{
+ if (inode->i_nlink) {
+ inode->__i_nlink = 0;
+ atomic_long_inc(&inode->i_sb->s_remove_count);
+ }
+}
+EXPORT_SYMBOL(clear_nlink);
+
+/**
+ * set_nlink - directly set an inode's link count
+ * @inode: inode
+ * @nlink: new nlink (should be non-zero)
+ *
+ * This is a low-level filesystem helper to replace any
+ * direct filesystem manipulation of i_nlink.
+ */
+void set_nlink(struct inode *inode, unsigned int nlink)
+{
+ if (!nlink) {
+ clear_nlink(inode);
+ } else {
+ /* Yes, some filesystems do change nlink from zero to one */
+ if (inode->i_nlink == 0)
+ atomic_long_dec(&inode->i_sb->s_remove_count);
+
+ inode->__i_nlink = nlink;
+ }
+}
+EXPORT_SYMBOL(set_nlink);
+
+/* from include/linux/fs.h */
+static inline void i_uid_write(struct inode *inode, uid_t uid)
+{
+ inode->i_uid.val = uid;
+}
+
+static inline void i_gid_write(struct inode *inode, gid_t gid)
+{
+ inode->i_gid.val = gid;
+}
+
+void unlock_new_inode(struct inode *inode)
+{
+ return;
+}
+#endif
+
+/*
+ * Maximum amount of memory we may 'kmalloc()' without worrying that we are
+ * allocating too much.
+ */
+#define UBIFS_KMALLOC_OK (128*1024)
+
+/* Slab cache for UBIFS inodes */
+struct kmem_cache *ubifs_inode_slab;
+
+#ifndef __UBOOT__
+/* UBIFS TNC shrinker description */
+static struct shrinker ubifs_shrinker_info = {
+ .scan_objects = ubifs_shrink_scan,
+ .count_objects = ubifs_shrink_count,
+ .seeks = DEFAULT_SEEKS,
+};
+#endif
+
+/**
+ * validate_inode - validate inode.
+ * @c: UBIFS file-system description object
+ * @inode: the inode to validate
+ *
+ * This is a helper function for 'ubifs_iget()' which validates various fields
+ * of a newly built inode to make sure they contain sane values and prevent
+ * possible vulnerabilities. Returns zero if the inode is all right and
+ * a non-zero error code if not.
+ */
+static int validate_inode(struct ubifs_info *c, const struct inode *inode)
+{
+ int err;
+ const struct ubifs_inode *ui = ubifs_inode(inode);
+
+ if (inode->i_size > c->max_inode_sz) {
+ ubifs_err(c, "inode is too large (%lld)",
+ (long long)inode->i_size);
+ return 1;
+ }
+
+ if (ui->compr_type >= UBIFS_COMPR_TYPES_CNT) {
+ ubifs_err(c, "unknown compression type %d", ui->compr_type);
+ return 2;
+ }
+
+ if (ui->xattr_names + ui->xattr_cnt > XATTR_LIST_MAX)
+ return 3;
+
+ if (ui->data_len < 0 || ui->data_len > UBIFS_MAX_INO_DATA)
+ return 4;
+
+ if (ui->xattr && !S_ISREG(inode->i_mode))
+ return 5;
+
+ if (!ubifs_compr_present(ui->compr_type)) {
+ ubifs_warn(c, "inode %lu uses '%s' compression, but it was not compiled in",
+ inode->i_ino, ubifs_compr_name(ui->compr_type));
+ }
+
+ err = dbg_check_dir(c, inode);
+ return err;
+}
+
+struct inode *ubifs_iget(struct super_block *sb, unsigned long inum)
+{
+ int err;
+ union ubifs_key key;
+ struct ubifs_ino_node *ino;
+ struct ubifs_info *c = sb->s_fs_info;
+ struct inode *inode;
+ struct ubifs_inode *ui;
+#ifdef __UBOOT__
+ int i;
+#endif
+
+ dbg_gen("inode %lu", inum);
+
+#ifdef __UBOOT__
+ /*
+ * U-Boot special handling of locked down inodes via recovery
+ * e.g. ubifs_recover_size()
+ */
+ for (i = 0; i < INODE_LOCKED_MAX; i++) {
+ /*
+ * Exit on last entry (NULL), inode not found in list
+ */
+ if (inodes_locked_down[i] == NULL)
+ break;
+
+ if (inodes_locked_down[i]->i_ino == inum) {
+ /*
+ * We found the locked down inode in our array,
+ * so just return this pointer instead of creating
+ * a new one.
+ */
+ return inodes_locked_down[i];
+ }
+ }
+#endif
+
+ inode = iget_locked(sb, inum);
+ if (!inode)
+ return ERR_PTR(-ENOMEM);
+ if (!(inode->i_state & I_NEW))
+ return inode;
+ ui = ubifs_inode(inode);
+
+ ino = kmalloc(UBIFS_MAX_INO_NODE_SZ, GFP_NOFS);
+ if (!ino) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ ino_key_init(c, &key, inode->i_ino);
+
+ err = ubifs_tnc_lookup(c, &key, ino);
+ if (err)
+ goto out_ino;
+
+ inode->i_flags |= (S_NOCMTIME | S_NOATIME);
+ set_nlink(inode, le32_to_cpu(ino->nlink));
+ i_uid_write(inode, le32_to_cpu(ino->uid));
+ i_gid_write(inode, le32_to_cpu(ino->gid));
+ inode->i_atime.tv_sec = (int64_t)le64_to_cpu(ino->atime_sec);
+ inode->i_atime.tv_nsec = le32_to_cpu(ino->atime_nsec);
+ inode->i_mtime.tv_sec = (int64_t)le64_to_cpu(ino->mtime_sec);
+ inode->i_mtime.tv_nsec = le32_to_cpu(ino->mtime_nsec);
+ inode->i_ctime.tv_sec = (int64_t)le64_to_cpu(ino->ctime_sec);
+ inode->i_ctime.tv_nsec = le32_to_cpu(ino->ctime_nsec);
+ inode->i_mode = le32_to_cpu(ino->mode);
+ inode->i_size = le64_to_cpu(ino->size);
+
+ ui->data_len = le32_to_cpu(ino->data_len);
+ ui->flags = le32_to_cpu(ino->flags);
+ ui->compr_type = le16_to_cpu(ino->compr_type);
+ ui->creat_sqnum = le64_to_cpu(ino->creat_sqnum);
+ ui->xattr_cnt = le32_to_cpu(ino->xattr_cnt);
+ ui->xattr_size = le32_to_cpu(ino->xattr_size);
+ ui->xattr_names = le32_to_cpu(ino->xattr_names);
+ ui->synced_i_size = ui->ui_size = inode->i_size;
+
+ ui->xattr = (ui->flags & UBIFS_XATTR_FL) ? 1 : 0;
+
+ err = validate_inode(c, inode);
+ if (err)
+ goto out_invalid;
+
+#ifndef __UBOOT__
+ switch (inode->i_mode & S_IFMT) {
+ case S_IFREG:
+ inode->i_mapping->a_ops = &ubifs_file_address_operations;
+ inode->i_op = &ubifs_file_inode_operations;
+ inode->i_fop = &ubifs_file_operations;
+ if (ui->xattr) {
+ ui->data = kmalloc(ui->data_len + 1, GFP_NOFS);
+ if (!ui->data) {
+ err = -ENOMEM;
+ goto out_ino;
+ }
+ memcpy(ui->data, ino->data, ui->data_len);
+ ((char *)ui->data)[ui->data_len] = '\0';
+ } else if (ui->data_len != 0) {
+ err = 10;
+ goto out_invalid;
+ }
+ break;
+ case S_IFDIR:
+ inode->i_op = &ubifs_dir_inode_operations;
+ inode->i_fop = &ubifs_dir_operations;
+ if (ui->data_len != 0) {
+ err = 11;
+ goto out_invalid;
+ }
+ break;
+ case S_IFLNK:
+ inode->i_op = &ubifs_symlink_inode_operations;
+ if (ui->data_len <= 0 || ui->data_len > UBIFS_MAX_INO_DATA) {
+ err = 12;
+ goto out_invalid;
+ }
+ ui->data = kmalloc(ui->data_len + 1, GFP_NOFS);
+ if (!ui->data) {
+ err = -ENOMEM;
+ goto out_ino;
+ }
+ memcpy(ui->data, ino->data, ui->data_len);
+ ((char *)ui->data)[ui->data_len] = '\0';
+ inode->i_link = ui->data;
+ break;
+ case S_IFBLK:
+ case S_IFCHR:
+ {
+ dev_t rdev;
+ union ubifs_dev_desc *dev;
+
+ ui->data = kmalloc(sizeof(union ubifs_dev_desc), GFP_NOFS);
+ if (!ui->data) {
+ err = -ENOMEM;
+ goto out_ino;
+ }
+
+ dev = (union ubifs_dev_desc *)ino->data;
+ if (ui->data_len == sizeof(dev->new))
+ rdev = new_decode_dev(le32_to_cpu(dev->new));
+ else if (ui->data_len == sizeof(dev->huge))
+ rdev = huge_decode_dev(le64_to_cpu(dev->huge));
+ else {
+ err = 13;
+ goto out_invalid;
+ }
+ memcpy(ui->data, ino->data, ui->data_len);
+ inode->i_op = &ubifs_file_inode_operations;
+ init_special_inode(inode, inode->i_mode, rdev);
+ break;
+ }
+ case S_IFSOCK:
+ case S_IFIFO:
+ inode->i_op = &ubifs_file_inode_operations;
+ init_special_inode(inode, inode->i_mode, 0);
+ if (ui->data_len != 0) {
+ err = 14;
+ goto out_invalid;
+ }
+ break;
+ default:
+ err = 15;
+ goto out_invalid;
+ }
+#else
+ if ((inode->i_mode & S_IFMT) == S_IFLNK) {
+ if (ui->data_len <= 0 || ui->data_len > UBIFS_MAX_INO_DATA) {
+ err = 12;
+ goto out_invalid;
+ }
+ ui->data = kmalloc(ui->data_len + 1, GFP_NOFS);
+ if (!ui->data) {
+ err = -ENOMEM;
+ goto out_ino;
+ }
+ memcpy(ui->data, ino->data, ui->data_len);
+ ((char *)ui->data)[ui->data_len] = '\0';
+ }
+#endif
+
+ kfree(ino);
+#ifndef __UBOOT__
+ ubifs_set_inode_flags(inode);
+#endif
+ unlock_new_inode(inode);
+ return inode;
+
+out_invalid:
+ ubifs_err(c, "inode %lu validation failed, error %d", inode->i_ino, err);
+ ubifs_dump_node(c, ino);
+ ubifs_dump_inode(c, inode);
+ err = -EINVAL;
+out_ino:
+ kfree(ino);
+out:
+ ubifs_err(c, "failed to read inode %lu, error %d", inode->i_ino, err);
+ iget_failed(inode);
+ return ERR_PTR(err);
+}
+
+static struct inode *ubifs_alloc_inode(struct super_block *sb)
+{
+ struct ubifs_inode *ui;
+
+ ui = kmem_cache_alloc(ubifs_inode_slab, GFP_NOFS);
+ if (!ui)
+ return NULL;
+
+ memset((void *)ui + sizeof(struct inode), 0,
+ sizeof(struct ubifs_inode) - sizeof(struct inode));
+ mutex_init(&ui->ui_mutex);
+ spin_lock_init(&ui->ui_lock);
+ return &ui->vfs_inode;
+};
+
+#ifndef __UBOOT__
+static void ubifs_i_callback(struct rcu_head *head)
+{
+ struct inode *inode = container_of(head, struct inode, i_rcu);
+ struct ubifs_inode *ui = ubifs_inode(inode);
+ kmem_cache_free(ubifs_inode_slab, ui);
+}
+
+static void ubifs_destroy_inode(struct inode *inode)
+{
+ struct ubifs_inode *ui = ubifs_inode(inode);
+
+ kfree(ui->data);
+ call_rcu(&inode->i_rcu, ubifs_i_callback);
+}
+
+/*
+ * Note, Linux write-back code calls this without 'i_mutex'.
+ */
+static int ubifs_write_inode(struct inode *inode, struct writeback_control *wbc)
+{
+ int err = 0;
+ struct ubifs_info *c = inode->i_sb->s_fs_info;
+ struct ubifs_inode *ui = ubifs_inode(inode);
+
+ ubifs_assert(!ui->xattr);
+ if (is_bad_inode(inode))
+ return 0;
+
+ mutex_lock(&ui->ui_mutex);
+ /*
+ * Due to races between write-back forced by budgeting
+ * (see 'sync_some_inodes()') and background write-back, the inode may
+ * have already been synchronized, do not do this again. This might
+ * also happen if it was synchronized in an VFS operation, e.g.
+ * 'ubifs_link()'.
+ */
+ if (!ui->dirty) {
+ mutex_unlock(&ui->ui_mutex);
+ return 0;
+ }
+
+ /*
+ * As an optimization, do not write orphan inodes to the media just
+ * because this is not needed.
+ */
+ dbg_gen("inode %lu, mode %#x, nlink %u",
+ inode->i_ino, (int)inode->i_mode, inode->i_nlink);
+ if (inode->i_nlink) {
+ err = ubifs_jnl_write_inode(c, inode);
+ if (err)
+ ubifs_err(c, "can't write inode %lu, error %d",
+ inode->i_ino, err);
+ else
+ err = dbg_check_inode_size(c, inode, ui->ui_size);
+ }
+
+ ui->dirty = 0;
+ mutex_unlock(&ui->ui_mutex);
+ ubifs_release_dirty_inode_budget(c, ui);
+ return err;
+}
+
+static void ubifs_evict_inode(struct inode *inode)
+{
+ int err;
+ struct ubifs_info *c = inode->i_sb->s_fs_info;
+ struct ubifs_inode *ui = ubifs_inode(inode);
+
+ if (ui->xattr)
+ /*
+ * Extended attribute inode deletions are fully handled in
+ * 'ubifs_removexattr()'. These inodes are special and have
+ * limited usage, so there is nothing to do here.
+ */
+ goto out;
+
+ dbg_gen("inode %lu, mode %#x", inode->i_ino, (int)inode->i_mode);
+ ubifs_assert(!atomic_read(&inode->i_count));
+
+ truncate_inode_pages_final(&inode->i_data);
+
+ if (inode->i_nlink)
+ goto done;
+
+ if (is_bad_inode(inode))
+ goto out;
+
+ ui->ui_size = inode->i_size = 0;
+ err = ubifs_jnl_delete_inode(c, inode);
+ if (err)
+ /*
+ * Worst case we have a lost orphan inode wasting space, so a
+ * simple error message is OK here.
+ */
+ ubifs_err(c, "can't delete inode %lu, error %d",
+ inode->i_ino, err);
+
+out:
+ if (ui->dirty)
+ ubifs_release_dirty_inode_budget(c, ui);
+ else {
+ /* We've deleted something - clean the "no space" flags */
+ c->bi.nospace = c->bi.nospace_rp = 0;
+ smp_wmb();
+ }
+done:
+ clear_inode(inode);
+}
+#endif
+
+static void ubifs_dirty_inode(struct inode *inode, int flags)
+{
+ struct ubifs_inode *ui = ubifs_inode(inode);
+
+ ubifs_assert(mutex_is_locked(&ui->ui_mutex));
+ if (!ui->dirty) {
+ ui->dirty = 1;
+ dbg_gen("inode %lu", inode->i_ino);
+ }
+}
+
+#ifndef __UBOOT__
+static int ubifs_statfs(struct dentry *dentry, struct kstatfs *buf)
+{
+ struct ubifs_info *c = dentry->d_sb->s_fs_info;
+ unsigned long long free;
+ __le32 *uuid = (__le32 *)c->uuid;
+
+ free = ubifs_get_free_space(c);
+ dbg_gen("free space %lld bytes (%lld blocks)",
+ free, free >> UBIFS_BLOCK_SHIFT);
+
+ buf->f_type = UBIFS_SUPER_MAGIC;
+ buf->f_bsize = UBIFS_BLOCK_SIZE;
+ buf->f_blocks = c->block_cnt;
+ buf->f_bfree = free >> UBIFS_BLOCK_SHIFT;
+ if (free > c->report_rp_size)
+ buf->f_bavail = (free - c->report_rp_size) >> UBIFS_BLOCK_SHIFT;
+ else
+ buf->f_bavail = 0;
+ buf->f_files = 0;
+ buf->f_ffree = 0;
+ buf->f_namelen = UBIFS_MAX_NLEN;
+ buf->f_fsid.val[0] = le32_to_cpu(uuid[0]) ^ le32_to_cpu(uuid[2]);
+ buf->f_fsid.val[1] = le32_to_cpu(uuid[1]) ^ le32_to_cpu(uuid[3]);
+ ubifs_assert(buf->f_bfree <= c->block_cnt);
+ return 0;
+}
+
+static int ubifs_show_options(struct seq_file *s, struct dentry *root)
+{
+ struct ubifs_info *c = root->d_sb->s_fs_info;
+
+ if (c->mount_opts.unmount_mode == 2)
+ seq_puts(s, ",fast_unmount");
+ else if (c->mount_opts.unmount_mode == 1)
+ seq_puts(s, ",norm_unmount");
+
+ if (c->mount_opts.bulk_read == 2)
+ seq_puts(s, ",bulk_read");
+ else if (c->mount_opts.bulk_read == 1)
+ seq_puts(s, ",no_bulk_read");
+
+ if (c->mount_opts.chk_data_crc == 2)
+ seq_puts(s, ",chk_data_crc");
+ else if (c->mount_opts.chk_data_crc == 1)
+ seq_puts(s, ",no_chk_data_crc");
+
+ if (c->mount_opts.override_compr) {
+ seq_printf(s, ",compr=%s",
+ ubifs_compr_name(c->mount_opts.compr_type));
+ }
+
+ return 0;
+}
+
+static int ubifs_sync_fs(struct super_block *sb, int wait)
+{
+ int i, err;
+ struct ubifs_info *c = sb->s_fs_info;
+
+ /*
+ * Zero @wait is just an advisory thing to help the file system shove
+ * lots of data into the queues, and there will be the second
+ * '->sync_fs()' call, with non-zero @wait.
+ */
+ if (!wait)
+ return 0;
+
+ /*
+ * Synchronize write buffers, because 'ubifs_run_commit()' does not
+ * do this if it waits for an already running commit.
+ */
+ for (i = 0; i < c->jhead_cnt; i++) {
+ err = ubifs_wbuf_sync(&c->jheads[i].wbuf);
+ if (err)
+ return err;
+ }
+
+ /*
+ * Strictly speaking, it is not necessary to commit the journal here,
+ * synchronizing write-buffers would be enough. But committing makes
+ * UBIFS free space predictions much more accurate, so we want to let
+ * the user be able to get more accurate results of 'statfs()' after
+ * they synchronize the file system.
+ */
+ err = ubifs_run_commit(c);
+ if (err)
+ return err;
+
+ return ubi_sync(c->vi.ubi_num);
+}
+#endif
+
+/**
+ * init_constants_early - initialize UBIFS constants.
+ * @c: UBIFS file-system description object
+ *
+ * This function initialize UBIFS constants which do not need the superblock to
+ * be read. It also checks that the UBI volume satisfies basic UBIFS
+ * requirements. Returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+static int init_constants_early(struct ubifs_info *c)
+{
+ if (c->vi.corrupted) {
+ ubifs_warn(c, "UBI volume is corrupted - read-only mode");
+ c->ro_media = 1;
+ }
+
+ if (c->di.ro_mode) {
+ ubifs_msg(c, "read-only UBI device");
+ c->ro_media = 1;
+ }
+
+ if (c->vi.vol_type == UBI_STATIC_VOLUME) {
+ ubifs_msg(c, "static UBI volume - read-only mode");
+ c->ro_media = 1;
+ }
+
+ c->leb_cnt = c->vi.size;
+ c->leb_size = c->vi.usable_leb_size;
+ c->leb_start = c->di.leb_start;
+ c->half_leb_size = c->leb_size / 2;
+ c->min_io_size = c->di.min_io_size;
+ c->min_io_shift = fls(c->min_io_size) - 1;
+ c->max_write_size = c->di.max_write_size;
+ c->max_write_shift = fls(c->max_write_size) - 1;
+
+ if (c->leb_size < UBIFS_MIN_LEB_SZ) {
+ ubifs_err(c, "too small LEBs (%d bytes), min. is %d bytes",
+ c->leb_size, UBIFS_MIN_LEB_SZ);
+ return -EINVAL;
+ }
+
+ if (c->leb_cnt < UBIFS_MIN_LEB_CNT) {
+ ubifs_err(c, "too few LEBs (%d), min. is %d",
+ c->leb_cnt, UBIFS_MIN_LEB_CNT);
+ return -EINVAL;
+ }
+
+ if (!is_power_of_2(c->min_io_size)) {
+ ubifs_err(c, "bad min. I/O size %d", c->min_io_size);
+ return -EINVAL;
+ }
+
+ /*
+ * Maximum write size has to be greater or equivalent to min. I/O
+ * size, and be multiple of min. I/O size.
+ */
+ if (c->max_write_size < c->min_io_size ||
+ c->max_write_size % c->min_io_size ||
+ !is_power_of_2(c->max_write_size)) {
+ ubifs_err(c, "bad write buffer size %d for %d min. I/O unit",
+ c->max_write_size, c->min_io_size);
+ return -EINVAL;
+ }
+
+ /*
+ * UBIFS aligns all node to 8-byte boundary, so to make function in
+ * io.c simpler, assume minimum I/O unit size to be 8 bytes if it is
+ * less than 8.
+ */
+ if (c->min_io_size < 8) {
+ c->min_io_size = 8;
+ c->min_io_shift = 3;
+ if (c->max_write_size < c->min_io_size) {
+ c->max_write_size = c->min_io_size;
+ c->max_write_shift = c->min_io_shift;
+ }
+ }
+
+ c->ref_node_alsz = ALIGN(UBIFS_REF_NODE_SZ, c->min_io_size);
+ c->mst_node_alsz = ALIGN(UBIFS_MST_NODE_SZ, c->min_io_size);
+
+ /*
+ * Initialize node length ranges which are mostly needed for node
+ * length validation.
+ */
+ c->ranges[UBIFS_PAD_NODE].len = UBIFS_PAD_NODE_SZ;
+ c->ranges[UBIFS_SB_NODE].len = UBIFS_SB_NODE_SZ;
+ c->ranges[UBIFS_MST_NODE].len = UBIFS_MST_NODE_SZ;
+ c->ranges[UBIFS_REF_NODE].len = UBIFS_REF_NODE_SZ;
+ c->ranges[UBIFS_TRUN_NODE].len = UBIFS_TRUN_NODE_SZ;
+ c->ranges[UBIFS_CS_NODE].len = UBIFS_CS_NODE_SZ;
+
+ c->ranges[UBIFS_INO_NODE].min_len = UBIFS_INO_NODE_SZ;
+ c->ranges[UBIFS_INO_NODE].max_len = UBIFS_MAX_INO_NODE_SZ;
+ c->ranges[UBIFS_ORPH_NODE].min_len =
+ UBIFS_ORPH_NODE_SZ + sizeof(__le64);
+ c->ranges[UBIFS_ORPH_NODE].max_len = c->leb_size;
+ c->ranges[UBIFS_DENT_NODE].min_len = UBIFS_DENT_NODE_SZ;
+ c->ranges[UBIFS_DENT_NODE].max_len = UBIFS_MAX_DENT_NODE_SZ;
+ c->ranges[UBIFS_XENT_NODE].min_len = UBIFS_XENT_NODE_SZ;
+ c->ranges[UBIFS_XENT_NODE].max_len = UBIFS_MAX_XENT_NODE_SZ;
+ c->ranges[UBIFS_DATA_NODE].min_len = UBIFS_DATA_NODE_SZ;
+ c->ranges[UBIFS_DATA_NODE].max_len = UBIFS_MAX_DATA_NODE_SZ;
+ /*
+ * Minimum indexing node size is amended later when superblock is
+ * read and the key length is known.
+ */
+ c->ranges[UBIFS_IDX_NODE].min_len = UBIFS_IDX_NODE_SZ + UBIFS_BRANCH_SZ;
+ /*
+ * Maximum indexing node size is amended later when superblock is
+ * read and the fanout is known.
+ */
+ c->ranges[UBIFS_IDX_NODE].max_len = INT_MAX;
+
+ /*
+ * Initialize dead and dark LEB space watermarks. See gc.c for comments
+ * about these values.
+ */
+ c->dead_wm = ALIGN(MIN_WRITE_SZ, c->min_io_size);
+ c->dark_wm = ALIGN(UBIFS_MAX_NODE_SZ, c->min_io_size);
+
+ /*
+ * Calculate how many bytes would be wasted at the end of LEB if it was
+ * fully filled with data nodes of maximum size. This is used in
+ * calculations when reporting free space.
+ */
+ c->leb_overhead = c->leb_size % UBIFS_MAX_DATA_NODE_SZ;
+
+ /* Buffer size for bulk-reads */
+ c->max_bu_buf_len = UBIFS_MAX_BULK_READ * UBIFS_MAX_DATA_NODE_SZ;
+ if (c->max_bu_buf_len > c->leb_size)
+ c->max_bu_buf_len = c->leb_size;
+ return 0;
+}
+
+/**
+ * bud_wbuf_callback - bud LEB write-buffer synchronization call-back.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB the write-buffer was synchronized to
+ * @free: how many free bytes left in this LEB
+ * @pad: how many bytes were padded
+ *
+ * This is a callback function which is called by the I/O unit when the
+ * write-buffer is synchronized. We need this to correctly maintain space
+ * accounting in bud logical eraseblocks. This function returns zero in case of
+ * success and a negative error code in case of failure.
+ *
+ * This function actually belongs to the journal, but we keep it here because
+ * we want to keep it static.
+ */
+static int bud_wbuf_callback(struct ubifs_info *c, int lnum, int free, int pad)
+{
+ return ubifs_update_one_lp(c, lnum, free, pad, 0, 0);
+}
+
+/*
+ * init_constants_sb - initialize UBIFS constants.
+ * @c: UBIFS file-system description object
+ *
+ * This is a helper function which initializes various UBIFS constants after
+ * the superblock has been read. It also checks various UBIFS parameters and
+ * makes sure they are all right. Returns zero in case of success and a
+ * negative error code in case of failure.
+ */
+static int init_constants_sb(struct ubifs_info *c)
+{
+ int tmp, err;
+ long long tmp64;
+
+ c->main_bytes = (long long)c->main_lebs * c->leb_size;
+ c->max_znode_sz = sizeof(struct ubifs_znode) +
+ c->fanout * sizeof(struct ubifs_zbranch);
+
+ tmp = ubifs_idx_node_sz(c, 1);
+ c->ranges[UBIFS_IDX_NODE].min_len = tmp;
+ c->min_idx_node_sz = ALIGN(tmp, 8);
+
+ tmp = ubifs_idx_node_sz(c, c->fanout);
+ c->ranges[UBIFS_IDX_NODE].max_len = tmp;
+ c->max_idx_node_sz = ALIGN(tmp, 8);
+
+ /* Make sure LEB size is large enough to fit full commit */
+ tmp = UBIFS_CS_NODE_SZ + UBIFS_REF_NODE_SZ * c->jhead_cnt;
+ tmp = ALIGN(tmp, c->min_io_size);
+ if (tmp > c->leb_size) {
+ ubifs_err(c, "too small LEB size %d, at least %d needed",
+ c->leb_size, tmp);
+ return -EINVAL;
+ }
+
+ /*
+ * Make sure that the log is large enough to fit reference nodes for
+ * all buds plus one reserved LEB.
+ */
+ tmp64 = c->max_bud_bytes + c->leb_size - 1;
+ c->max_bud_cnt = div_u64(tmp64, c->leb_size);
+ tmp = (c->ref_node_alsz * c->max_bud_cnt + c->leb_size - 1);
+ tmp /= c->leb_size;
+ tmp += 1;
+ if (c->log_lebs < tmp) {
+ ubifs_err(c, "too small log %d LEBs, required min. %d LEBs",
+ c->log_lebs, tmp);
+ return -EINVAL;
+ }
+
+ /*
+ * When budgeting we assume worst-case scenarios when the pages are not
+ * be compressed and direntries are of the maximum size.
+ *
+ * Note, data, which may be stored in inodes is budgeted separately, so
+ * it is not included into 'c->bi.inode_budget'.
+ */
+ c->bi.page_budget = UBIFS_MAX_DATA_NODE_SZ * UBIFS_BLOCKS_PER_PAGE;
+ c->bi.inode_budget = UBIFS_INO_NODE_SZ;
+ c->bi.dent_budget = UBIFS_MAX_DENT_NODE_SZ;
+
+ /*
+ * When the amount of flash space used by buds becomes
+ * 'c->max_bud_bytes', UBIFS just blocks all writers and starts commit.
+ * The writers are unblocked when the commit is finished. To avoid
+ * writers to be blocked UBIFS initiates background commit in advance,
+ * when number of bud bytes becomes above the limit defined below.
+ */
+ c->bg_bud_bytes = (c->max_bud_bytes * 13) >> 4;
+
+ /*
+ * Ensure minimum journal size. All the bytes in the journal heads are
+ * considered to be used, when calculating the current journal usage.
+ * Consequently, if the journal is too small, UBIFS will treat it as
+ * always full.
+ */
+ tmp64 = (long long)(c->jhead_cnt + 1) * c->leb_size + 1;
+ if (c->bg_bud_bytes < tmp64)
+ c->bg_bud_bytes = tmp64;
+ if (c->max_bud_bytes < tmp64 + c->leb_size)
+ c->max_bud_bytes = tmp64 + c->leb_size;
+
+ err = ubifs_calc_lpt_geom(c);
+ if (err)
+ return err;
+
+ /* Initialize effective LEB size used in budgeting calculations */
+ c->idx_leb_size = c->leb_size - c->max_idx_node_sz;
+ return 0;
+}
+
+/*
+ * init_constants_master - initialize UBIFS constants.
+ * @c: UBIFS file-system description object
+ *
+ * This is a helper function which initializes various UBIFS constants after
+ * the master node has been read. It also checks various UBIFS parameters and
+ * makes sure they are all right.
+ */
+static void init_constants_master(struct ubifs_info *c)
+{
+ long long tmp64;
+
+ c->bi.min_idx_lebs = ubifs_calc_min_idx_lebs(c);
+ c->report_rp_size = ubifs_reported_space(c, c->rp_size);
+
+ /*
+ * Calculate total amount of FS blocks. This number is not used
+ * internally because it does not make much sense for UBIFS, but it is
+ * necessary to report something for the 'statfs()' call.
+ *
+ * Subtract the LEB reserved for GC, the LEB which is reserved for
+ * deletions, minimum LEBs for the index, and assume only one journal
+ * head is available.
+ */
+ tmp64 = c->main_lebs - 1 - 1 - MIN_INDEX_LEBS - c->jhead_cnt + 1;
+ tmp64 *= (long long)c->leb_size - c->leb_overhead;
+ tmp64 = ubifs_reported_space(c, tmp64);
+ c->block_cnt = tmp64 >> UBIFS_BLOCK_SHIFT;
+}
+
+/**
+ * take_gc_lnum - reserve GC LEB.
+ * @c: UBIFS file-system description object
+ *
+ * This function ensures that the LEB reserved for garbage collection is marked
+ * as "taken" in lprops. We also have to set free space to LEB size and dirty
+ * space to zero, because lprops may contain out-of-date information if the
+ * file-system was un-mounted before it has been committed. This function
+ * returns zero in case of success and a negative error code in case of
+ * failure.
+ */
+static int take_gc_lnum(struct ubifs_info *c)
+{
+ int err;
+
+ if (c->gc_lnum == -1) {
+ ubifs_err(c, "no LEB for GC");
+ return -EINVAL;
+ }
+
+ /* And we have to tell lprops that this LEB is taken */
+ err = ubifs_change_one_lp(c, c->gc_lnum, c->leb_size, 0,
+ LPROPS_TAKEN, 0, 0);
+ return err;
+}
+
+/**
+ * alloc_wbufs - allocate write-buffers.
+ * @c: UBIFS file-system description object
+ *
+ * This helper function allocates and initializes UBIFS write-buffers. Returns
+ * zero in case of success and %-ENOMEM in case of failure.
+ */
+static int alloc_wbufs(struct ubifs_info *c)
+{
+ int i, err;
+
+ c->jheads = kcalloc(c->jhead_cnt, sizeof(struct ubifs_jhead),
+ GFP_KERNEL);
+ if (!c->jheads)
+ return -ENOMEM;
+
+ /* Initialize journal heads */
+ for (i = 0; i < c->jhead_cnt; i++) {
+ INIT_LIST_HEAD(&c->jheads[i].buds_list);
+ err = ubifs_wbuf_init(c, &c->jheads[i].wbuf);
+ if (err)
+ return err;
+
+ c->jheads[i].wbuf.sync_callback = &bud_wbuf_callback;
+ c->jheads[i].wbuf.jhead = i;
+ c->jheads[i].grouped = 1;
+ }
+
+ /*
+ * Garbage Collector head does not need to be synchronized by timer.
+ * Also GC head nodes are not grouped.
+ */
+ c->jheads[GCHD].wbuf.no_timer = 1;
+ c->jheads[GCHD].grouped = 0;
+
+ return 0;
+}
+
+/**
+ * free_wbufs - free write-buffers.
+ * @c: UBIFS file-system description object
+ */
+static void free_wbufs(struct ubifs_info *c)
+{
+ int i;
+
+ if (c->jheads) {
+ for (i = 0; i < c->jhead_cnt; i++) {
+ kfree(c->jheads[i].wbuf.buf);
+ kfree(c->jheads[i].wbuf.inodes);
+ }
+ kfree(c->jheads);
+ c->jheads = NULL;
+ }
+}
+
+/**
+ * free_orphans - free orphans.
+ * @c: UBIFS file-system description object
+ */
+static void free_orphans(struct ubifs_info *c)
+{
+ struct ubifs_orphan *orph;
+
+ while (c->orph_dnext) {
+ orph = c->orph_dnext;
+ c->orph_dnext = orph->dnext;
+ list_del(&orph->list);
+ kfree(orph);
+ }
+
+ while (!list_empty(&c->orph_list)) {
+ orph = list_entry(c->orph_list.next, struct ubifs_orphan, list);
+ list_del(&orph->list);
+ kfree(orph);
+ ubifs_err(c, "orphan list not empty at unmount");
+ }
+
+ vfree(c->orph_buf);
+ c->orph_buf = NULL;
+}
+
+/**
+ * free_buds - free per-bud objects.
+ * @c: UBIFS file-system description object
+ */
+static void free_buds(struct ubifs_info *c)
+{
+ struct ubifs_bud *bud, *n;
+
+ rbtree_postorder_for_each_entry_safe(bud, n, &c->buds, rb)
+ kfree(bud);
+}
+
+/**
+ * check_volume_empty - check if the UBI volume is empty.
+ * @c: UBIFS file-system description object
+ *
+ * This function checks if the UBIFS volume is empty by looking if its LEBs are
+ * mapped or not. The result of checking is stored in the @c->empty variable.
+ * Returns zero in case of success and a negative error code in case of
+ * failure.
+ */
+static int check_volume_empty(struct ubifs_info *c)
+{
+ int lnum, err;
+
+ c->empty = 1;
+ for (lnum = 0; lnum < c->leb_cnt; lnum++) {
+ err = ubifs_is_mapped(c, lnum);
+ if (unlikely(err < 0))
+ return err;
+ if (err == 1) {
+ c->empty = 0;
+ break;
+ }
+
+ cond_resched();
+ }
+
+ return 0;
+}
+
+/*
+ * UBIFS mount options.
+ *
+ * Opt_fast_unmount: do not run a journal commit before un-mounting
+ * Opt_norm_unmount: run a journal commit before un-mounting
+ * Opt_bulk_read: enable bulk-reads
+ * Opt_no_bulk_read: disable bulk-reads
+ * Opt_chk_data_crc: check CRCs when reading data nodes
+ * Opt_no_chk_data_crc: do not check CRCs when reading data nodes
+ * Opt_override_compr: override default compressor
+ * Opt_err: just end of array marker
+ */
+enum {
+ Opt_fast_unmount,
+ Opt_norm_unmount,
+ Opt_bulk_read,
+ Opt_no_bulk_read,
+ Opt_chk_data_crc,
+ Opt_no_chk_data_crc,
+ Opt_override_compr,
+ Opt_err,
+};
+
+#ifndef __UBOOT__
+static const match_table_t tokens = {
+ {Opt_fast_unmount, "fast_unmount"},
+ {Opt_norm_unmount, "norm_unmount"},
+ {Opt_bulk_read, "bulk_read"},
+ {Opt_no_bulk_read, "no_bulk_read"},
+ {Opt_chk_data_crc, "chk_data_crc"},
+ {Opt_no_chk_data_crc, "no_chk_data_crc"},
+ {Opt_override_compr, "compr=%s"},
+ {Opt_err, NULL},
+};
+
+/**
+ * parse_standard_option - parse a standard mount option.
+ * @option: the option to parse
+ *
+ * Normally, standard mount options like "sync" are passed to file-systems as
+ * flags. However, when a "rootflags=" kernel boot parameter is used, they may
+ * be present in the options string. This function tries to deal with this
+ * situation and parse standard options. Returns 0 if the option was not
+ * recognized, and the corresponding integer flag if it was.
+ *
+ * UBIFS is only interested in the "sync" option, so do not check for anything
+ * else.
+ */
+static int parse_standard_option(const char *option)
+{
+
+ pr_notice("UBIFS: parse %s\n", option);
+ if (!strcmp(option, "sync"))
+ return MS_SYNCHRONOUS;
+ return 0;
+}
+
+/**
+ * ubifs_parse_options - parse mount parameters.
+ * @c: UBIFS file-system description object
+ * @options: parameters to parse
+ * @is_remount: non-zero if this is FS re-mount
+ *
+ * This function parses UBIFS mount options and returns zero in case success
+ * and a negative error code in case of failure.
+ */
+static int ubifs_parse_options(struct ubifs_info *c, char *options,
+ int is_remount)
+{
+ char *p;
+ substring_t args[MAX_OPT_ARGS];
+
+ if (!options)
+ return 0;
+
+ while ((p = strsep(&options, ","))) {
+ int token;
+
+ if (!*p)
+ continue;
+
+ token = match_token(p, tokens, args);
+ switch (token) {
+ /*
+ * %Opt_fast_unmount and %Opt_norm_unmount options are ignored.
+ * We accept them in order to be backward-compatible. But this
+ * should be removed at some point.
+ */
+ case Opt_fast_unmount:
+ c->mount_opts.unmount_mode = 2;
+ break;
+ case Opt_norm_unmount:
+ c->mount_opts.unmount_mode = 1;
+ break;
+ case Opt_bulk_read:
+ c->mount_opts.bulk_read = 2;
+ c->bulk_read = 1;
+ break;
+ case Opt_no_bulk_read:
+ c->mount_opts.bulk_read = 1;
+ c->bulk_read = 0;
+ break;
+ case Opt_chk_data_crc:
+ c->mount_opts.chk_data_crc = 2;
+ c->no_chk_data_crc = 0;
+ break;
+ case Opt_no_chk_data_crc:
+ c->mount_opts.chk_data_crc = 1;
+ c->no_chk_data_crc = 1;
+ break;
+ case Opt_override_compr:
+ {
+ char *name = match_strdup(&args[0]);
+
+ if (!name)
+ return -ENOMEM;
+ if (!strcmp(name, "none"))
+ c->mount_opts.compr_type = UBIFS_COMPR_NONE;
+ else if (!strcmp(name, "lzo"))
+ c->mount_opts.compr_type = UBIFS_COMPR_LZO;
+ else if (!strcmp(name, "zlib"))
+ c->mount_opts.compr_type = UBIFS_COMPR_ZLIB;
+ else {
+ ubifs_err(c, "unknown compressor \"%s\"", name); //FIXME: is c ready?
+ kfree(name);
+ return -EINVAL;
+ }
+ kfree(name);
+ c->mount_opts.override_compr = 1;
+ c->default_compr = c->mount_opts.compr_type;
+ break;
+ }
+ default:
+ {
+ unsigned long flag;
+ struct super_block *sb = c->vfs_sb;
+
+ flag = parse_standard_option(p);
+ if (!flag) {
+ ubifs_err(c, "unrecognized mount option \"%s\" or missing value",
+ p);
+ return -EINVAL;
+ }
+ sb->s_flags |= flag;
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+#endif
+
+/**
+ * destroy_journal - destroy journal data structures.
+ * @c: UBIFS file-system description object
+ *
+ * This function destroys journal data structures including those that may have
+ * been created by recovery functions.
+ */
+static void destroy_journal(struct ubifs_info *c)
+{
+ while (!list_empty(&c->unclean_leb_list)) {
+ struct ubifs_unclean_leb *ucleb;
+
+ ucleb = list_entry(c->unclean_leb_list.next,
+ struct ubifs_unclean_leb, list);
+ list_del(&ucleb->list);
+ kfree(ucleb);
+ }
+ while (!list_empty(&c->old_buds)) {
+ struct ubifs_bud *bud;
+
+ bud = list_entry(c->old_buds.next, struct ubifs_bud, list);
+ list_del(&bud->list);
+ kfree(bud);
+ }
+ ubifs_destroy_idx_gc(c);
+ ubifs_destroy_size_tree(c);
+ ubifs_tnc_close(c);
+ free_buds(c);
+}
+
+/**
+ * bu_init - initialize bulk-read information.
+ * @c: UBIFS file-system description object
+ */
+static void bu_init(struct ubifs_info *c)
+{
+ ubifs_assert(c->bulk_read == 1);
+
+ if (c->bu.buf)
+ return; /* Already initialized */
+
+again:
+ c->bu.buf = kmalloc(c->max_bu_buf_len, GFP_KERNEL | __GFP_NOWARN);
+ if (!c->bu.buf) {
+ if (c->max_bu_buf_len > UBIFS_KMALLOC_OK) {
+ c->max_bu_buf_len = UBIFS_KMALLOC_OK;
+ goto again;
+ }
+
+ /* Just disable bulk-read */
+ ubifs_warn(c, "cannot allocate %d bytes of memory for bulk-read, disabling it",
+ c->max_bu_buf_len);
+ c->mount_opts.bulk_read = 1;
+ c->bulk_read = 0;
+ return;
+ }
+}
+
+#ifndef __UBOOT__
+/**
+ * check_free_space - check if there is enough free space to mount.
+ * @c: UBIFS file-system description object
+ *
+ * This function makes sure UBIFS has enough free space to be mounted in
+ * read/write mode. UBIFS must always have some free space to allow deletions.
+ */
+static int check_free_space(struct ubifs_info *c)
+{
+ ubifs_assert(c->dark_wm > 0);
+ if (c->lst.total_free + c->lst.total_dirty < c->dark_wm) {
+ ubifs_err(c, "insufficient free space to mount in R/W mode");
+ ubifs_dump_budg(c, &c->bi);
+ ubifs_dump_lprops(c);
+ return -ENOSPC;
+ }
+ return 0;
+}
+#endif
+
+/**
+ * mount_ubifs - mount UBIFS file-system.
+ * @c: UBIFS file-system description object
+ *
+ * This function mounts UBIFS file system. Returns zero in case of success and
+ * a negative error code in case of failure.
+ */
+static int mount_ubifs(struct ubifs_info *c)
+{
+ int err;
+ long long x;
+#ifndef CONFIG_UBIFS_SILENCE_MSG
+ long long y;
+#endif
+ size_t sz;
+
+ c->ro_mount = !!(c->vfs_sb->s_flags & MS_RDONLY);
+ /* Suppress error messages while probing if MS_SILENT is set */
+ c->probing = !!(c->vfs_sb->s_flags & MS_SILENT);
+#ifdef __UBOOT__
+ if (!c->ro_mount) {
+ printf("UBIFS: only ro mode in U-Boot allowed.\n");
+ return -EACCES;
+ }
+#endif
+
+ err = init_constants_early(c);
+ if (err)
+ return err;
+
+ err = ubifs_debugging_init(c);
+ if (err)
+ return err;
+
+ err = check_volume_empty(c);
+ if (err)
+ goto out_free;
+
+ if (c->empty && (c->ro_mount || c->ro_media)) {
+ /*
+ * This UBI volume is empty, and read-only, or the file system
+ * is mounted read-only - we cannot format it.
+ */
+ ubifs_err(c, "can't format empty UBI volume: read-only %s",
+ c->ro_media ? "UBI volume" : "mount");
+ err = -EROFS;
+ goto out_free;
+ }
+
+ if (c->ro_media && !c->ro_mount) {
+ ubifs_err(c, "cannot mount read-write - read-only media");
+ err = -EROFS;
+ goto out_free;
+ }
+
+ /*
+ * The requirement for the buffer is that it should fit indexing B-tree
+ * height amount of integers. We assume the height if the TNC tree will
+ * never exceed 64.
+ */
+ err = -ENOMEM;
+ c->bottom_up_buf = kmalloc(BOTTOM_UP_HEIGHT * sizeof(int), GFP_KERNEL);
+ if (!c->bottom_up_buf)
+ goto out_free;
+
+ c->sbuf = vmalloc(c->leb_size);
+ if (!c->sbuf)
+ goto out_free;
+
+#ifndef __UBOOT__
+ if (!c->ro_mount) {
+ c->ileb_buf = vmalloc(c->leb_size);
+ if (!c->ileb_buf)
+ goto out_free;
+ }
+#endif
+
+ if (c->bulk_read == 1)
+ bu_init(c);
+
+#ifndef __UBOOT__
+ if (!c->ro_mount) {
+ c->write_reserve_buf = kmalloc(COMPRESSED_DATA_NODE_BUF_SZ,
+ GFP_KERNEL);
+ if (!c->write_reserve_buf)
+ goto out_free;
+ }
+#endif
+
+ c->mounting = 1;
+
+ err = ubifs_read_superblock(c);
+ if (err)
+ goto out_free;
+
+ c->probing = 0;
+
+ /*
+ * Make sure the compressor which is set as default in the superblock
+ * or overridden by mount options is actually compiled in.
+ */
+ if (!ubifs_compr_present(c->default_compr)) {
+ ubifs_err(c, "'compressor \"%s\" is not compiled in",
+ ubifs_compr_name(c->default_compr));
+ err = -ENOTSUPP;
+ goto out_free;
+ }
+
+ err = init_constants_sb(c);
+ if (err)
+ goto out_free;
+
+ sz = ALIGN(c->max_idx_node_sz, c->min_io_size);
+ sz = ALIGN(sz + c->max_idx_node_sz, c->min_io_size);
+ c->cbuf = kmalloc(sz, GFP_NOFS);
+ if (!c->cbuf) {
+ err = -ENOMEM;
+ goto out_free;
+ }
+
+ err = alloc_wbufs(c);
+ if (err)
+ goto out_cbuf;
+
+ sprintf(c->bgt_name, BGT_NAME_PATTERN, c->vi.ubi_num, c->vi.vol_id);
+#ifndef __UBOOT__
+ if (!c->ro_mount) {
+ /* Create background thread */
+ c->bgt = kthread_create(ubifs_bg_thread, c, "%s", c->bgt_name);
+ if (IS_ERR(c->bgt)) {
+ err = PTR_ERR(c->bgt);
+ c->bgt = NULL;
+ ubifs_err(c, "cannot spawn \"%s\", error %d",
+ c->bgt_name, err);
+ goto out_wbufs;
+ }
+ wake_up_process(c->bgt);
+ }
+#endif
+
+ err = ubifs_read_master(c);
+ if (err)
+ goto out_master;
+
+ init_constants_master(c);
+
+ if ((c->mst_node->flags & cpu_to_le32(UBIFS_MST_DIRTY)) != 0) {
+ ubifs_msg(c, "recovery needed");
+ c->need_recovery = 1;
+ }
+
+#ifndef __UBOOT__
+ if (c->need_recovery && !c->ro_mount) {
+ err = ubifs_recover_inl_heads(c, c->sbuf);
+ if (err)
+ goto out_master;
+ }
+#endif
+
+ err = ubifs_lpt_init(c, 1, !c->ro_mount);
+ if (err)
+ goto out_master;
+
+#ifndef __UBOOT__
+ if (!c->ro_mount && c->space_fixup) {
+ err = ubifs_fixup_free_space(c);
+ if (err)
+ goto out_lpt;
+ }
+
+ if (!c->ro_mount && !c->need_recovery) {
+ /*
+ * Set the "dirty" flag so that if we reboot uncleanly we
+ * will notice this immediately on the next mount.
+ */
+ c->mst_node->flags |= cpu_to_le32(UBIFS_MST_DIRTY);
+ err = ubifs_write_master(c);
+ if (err)
+ goto out_lpt;
+ }
+#endif
+
+ err = dbg_check_idx_size(c, c->bi.old_idx_sz);
+ if (err)
+ goto out_lpt;
+
+ err = ubifs_replay_journal(c);
+ if (err)
+ goto out_journal;
+
+ /* Calculate 'min_idx_lebs' after journal replay */
+ c->bi.min_idx_lebs = ubifs_calc_min_idx_lebs(c);
+
+ err = ubifs_mount_orphans(c, c->need_recovery, c->ro_mount);
+ if (err)
+ goto out_orphans;
+
+ if (!c->ro_mount) {
+#ifndef __UBOOT__
+ int lnum;
+
+ err = check_free_space(c);
+ if (err)
+ goto out_orphans;
+
+ /* Check for enough log space */
+ lnum = c->lhead_lnum + 1;
+ if (lnum >= UBIFS_LOG_LNUM + c->log_lebs)
+ lnum = UBIFS_LOG_LNUM;
+ if (lnum == c->ltail_lnum) {
+ err = ubifs_consolidate_log(c);
+ if (err)
+ goto out_orphans;
+ }
+
+ if (c->need_recovery) {
+ err = ubifs_recover_size(c);
+ if (err)
+ goto out_orphans;
+ err = ubifs_rcvry_gc_commit(c);
+ if (err)
+ goto out_orphans;
+ } else {
+ err = take_gc_lnum(c);
+ if (err)
+ goto out_orphans;
+
+ /*
+ * GC LEB may contain garbage if there was an unclean
+ * reboot, and it should be un-mapped.
+ */
+ err = ubifs_leb_unmap(c, c->gc_lnum);
+ if (err)
+ goto out_orphans;
+ }
+
+ err = dbg_check_lprops(c);
+ if (err)
+ goto out_orphans;
+#endif
+ } else if (c->need_recovery) {
+ err = ubifs_recover_size(c);
+ if (err)
+ goto out_orphans;
+ } else {
+ /*
+ * Even if we mount read-only, we have to set space in GC LEB
+ * to proper value because this affects UBIFS free space
+ * reporting. We do not want to have a situation when
+ * re-mounting from R/O to R/W changes amount of free space.
+ */
+ err = take_gc_lnum(c);
+ if (err)
+ goto out_orphans;
+ }
+
+#ifndef __UBOOT__
+ spin_lock(&ubifs_infos_lock);
+ list_add_tail(&c->infos_list, &ubifs_infos);
+ spin_unlock(&ubifs_infos_lock);
+#endif
+
+ if (c->need_recovery) {
+ if (c->ro_mount)
+ ubifs_msg(c, "recovery deferred");
+ else {
+ c->need_recovery = 0;
+ ubifs_msg(c, "recovery completed");
+ /*
+ * GC LEB has to be empty and taken at this point. But
+ * the journal head LEBs may also be accounted as
+ * "empty taken" if they are empty.
+ */
+ ubifs_assert(c->lst.taken_empty_lebs > 0);
+ }
+ } else
+ ubifs_assert(c->lst.taken_empty_lebs > 0);
+
+ err = dbg_check_filesystem(c);
+ if (err)
+ goto out_infos;
+
+ err = dbg_debugfs_init_fs(c);
+ if (err)
+ goto out_infos;
+
+ c->mounting = 0;
+
+ ubifs_msg(c, "UBIFS: mounted UBI device %d, volume %d, name \"%s\"%s",
+ c->vi.ubi_num, c->vi.vol_id, c->vi.name,
+ c->ro_mount ? ", R/O mode" : "");
+ x = (long long)c->main_lebs * c->leb_size;
+#ifndef CONFIG_UBIFS_SILENCE_MSG
+ y = (long long)c->log_lebs * c->leb_size + c->max_bud_bytes;
+#endif
+ ubifs_msg(c, "LEB size: %d bytes (%d KiB), min./max. I/O unit sizes: %d bytes/%d bytes",
+ c->leb_size, c->leb_size >> 10, c->min_io_size,
+ c->max_write_size);
+ ubifs_msg(c, "FS size: %lld bytes (%lld MiB, %d LEBs), journal size %lld bytes (%lld MiB, %d LEBs)",
+ x, x >> 20, c->main_lebs,
+ y, y >> 20, c->log_lebs + c->max_bud_cnt);
+ ubifs_msg(c, "reserved for root: %llu bytes (%llu KiB)",
+ c->report_rp_size, c->report_rp_size >> 10);
+ ubifs_msg(c, "media format: w%d/r%d (latest is w%d/r%d), UUID %pUB%s",
+ c->fmt_version, c->ro_compat_version,
+ UBIFS_FORMAT_VERSION, UBIFS_RO_COMPAT_VERSION, c->uuid,
+ c->big_lpt ? ", big LPT model" : ", small LPT model");
+
+ dbg_gen("default compressor: %s", ubifs_compr_name(c->default_compr));
+ dbg_gen("data journal heads: %d",
+ c->jhead_cnt - NONDATA_JHEADS_CNT);
+ dbg_gen("log LEBs: %d (%d - %d)",
+ c->log_lebs, UBIFS_LOG_LNUM, c->log_last);
+ dbg_gen("LPT area LEBs: %d (%d - %d)",
+ c->lpt_lebs, c->lpt_first, c->lpt_last);
+ dbg_gen("orphan area LEBs: %d (%d - %d)",
+ c->orph_lebs, c->orph_first, c->orph_last);
+ dbg_gen("main area LEBs: %d (%d - %d)",
+ c->main_lebs, c->main_first, c->leb_cnt - 1);
+ dbg_gen("index LEBs: %d", c->lst.idx_lebs);
+ dbg_gen("total index bytes: %lld (%lld KiB, %lld MiB)",
+ c->bi.old_idx_sz, c->bi.old_idx_sz >> 10,
+ c->bi.old_idx_sz >> 20);
+ dbg_gen("key hash type: %d", c->key_hash_type);
+ dbg_gen("tree fanout: %d", c->fanout);
+ dbg_gen("reserved GC LEB: %d", c->gc_lnum);
+ dbg_gen("max. znode size %d", c->max_znode_sz);
+ dbg_gen("max. index node size %d", c->max_idx_node_sz);
+ dbg_gen("node sizes: data %zu, inode %zu, dentry %zu",
+ UBIFS_DATA_NODE_SZ, UBIFS_INO_NODE_SZ, UBIFS_DENT_NODE_SZ);
+ dbg_gen("node sizes: trun %zu, sb %zu, master %zu",
+ UBIFS_TRUN_NODE_SZ, UBIFS_SB_NODE_SZ, UBIFS_MST_NODE_SZ);
+ dbg_gen("node sizes: ref %zu, cmt. start %zu, orph %zu",
+ UBIFS_REF_NODE_SZ, UBIFS_CS_NODE_SZ, UBIFS_ORPH_NODE_SZ);
+ dbg_gen("max. node sizes: data %zu, inode %zu dentry %zu, idx %d",
+ UBIFS_MAX_DATA_NODE_SZ, UBIFS_MAX_INO_NODE_SZ,
+ UBIFS_MAX_DENT_NODE_SZ, ubifs_idx_node_sz(c, c->fanout));
+ dbg_gen("dead watermark: %d", c->dead_wm);
+ dbg_gen("dark watermark: %d", c->dark_wm);
+ dbg_gen("LEB overhead: %d", c->leb_overhead);
+ x = (long long)c->main_lebs * c->dark_wm;
+ dbg_gen("max. dark space: %lld (%lld KiB, %lld MiB)",
+ x, x >> 10, x >> 20);
+ dbg_gen("maximum bud bytes: %lld (%lld KiB, %lld MiB)",
+ c->max_bud_bytes, c->max_bud_bytes >> 10,
+ c->max_bud_bytes >> 20);
+ dbg_gen("BG commit bud bytes: %lld (%lld KiB, %lld MiB)",
+ c->bg_bud_bytes, c->bg_bud_bytes >> 10,
+ c->bg_bud_bytes >> 20);
+ dbg_gen("current bud bytes %lld (%lld KiB, %lld MiB)",
+ c->bud_bytes, c->bud_bytes >> 10, c->bud_bytes >> 20);
+ dbg_gen("max. seq. number: %llu", c->max_sqnum);
+ dbg_gen("commit number: %llu", c->cmt_no);
+
+ return 0;
+
+out_infos:
+ spin_lock(&ubifs_infos_lock);
+ list_del(&c->infos_list);
+ spin_unlock(&ubifs_infos_lock);
+out_orphans:
+ free_orphans(c);
+out_journal:
+ destroy_journal(c);
+out_lpt:
+ ubifs_lpt_free(c, 0);
+out_master:
+ kfree(c->mst_node);
+ kfree(c->rcvrd_mst_node);
+ if (c->bgt)
+ kthread_stop(c->bgt);
+#ifndef __UBOOT__
+out_wbufs:
+#endif
+ free_wbufs(c);
+out_cbuf:
+ kfree(c->cbuf);
+out_free:
+ kfree(c->write_reserve_buf);
+ kfree(c->bu.buf);
+ vfree(c->ileb_buf);
+ vfree(c->sbuf);
+ kfree(c->bottom_up_buf);
+ ubifs_debugging_exit(c);
+ return err;
+}
+
+/**
+ * ubifs_umount - un-mount UBIFS file-system.
+ * @c: UBIFS file-system description object
+ *
+ * Note, this function is called to free allocated resourced when un-mounting,
+ * as well as free resources when an error occurred while we were half way
+ * through mounting (error path cleanup function). So it has to make sure the
+ * resource was actually allocated before freeing it.
+ */
+#ifndef __UBOOT__
+static void ubifs_umount(struct ubifs_info *c)
+#else
+void ubifs_umount(struct ubifs_info *c)
+#endif
+{
+ dbg_gen("un-mounting UBI device %d, volume %d", c->vi.ubi_num,
+ c->vi.vol_id);
+
+ dbg_debugfs_exit_fs(c);
+ spin_lock(&ubifs_infos_lock);
+ list_del(&c->infos_list);
+ spin_unlock(&ubifs_infos_lock);
+
+#ifndef __UBOOT__
+ if (c->bgt)
+ kthread_stop(c->bgt);
+
+ destroy_journal(c);
+#endif
+ free_wbufs(c);
+ free_orphans(c);
+ ubifs_lpt_free(c, 0);
+
+ kfree(c->cbuf);
+ kfree(c->rcvrd_mst_node);
+ kfree(c->mst_node);
+ kfree(c->write_reserve_buf);
+ kfree(c->bu.buf);
+ vfree(c->ileb_buf);
+ vfree(c->sbuf);
+ kfree(c->bottom_up_buf);
+ ubifs_debugging_exit(c);
+#ifdef __UBOOT__
+ /* Finally free U-Boot's global copy of superblock */
+ if (ubifs_sb != NULL) {
+ free(ubifs_sb->s_fs_info);
+ free(ubifs_sb);
+ }
+#endif
+}
+
+#ifndef __UBOOT__
+/**
+ * ubifs_remount_rw - re-mount in read-write mode.
+ * @c: UBIFS file-system description object
+ *
+ * UBIFS avoids allocating many unnecessary resources when mounted in read-only
+ * mode. This function allocates the needed resources and re-mounts UBIFS in
+ * read-write mode.
+ */
+static int ubifs_remount_rw(struct ubifs_info *c)
+{
+ int err, lnum;
+
+ if (c->rw_incompat) {
+ ubifs_err(c, "the file-system is not R/W-compatible");
+ ubifs_msg(c, "on-flash format version is w%d/r%d, but software only supports up to version w%d/r%d",
+ c->fmt_version, c->ro_compat_version,
+ UBIFS_FORMAT_VERSION, UBIFS_RO_COMPAT_VERSION);
+ return -EROFS;
+ }
+
+ mutex_lock(&c->umount_mutex);
+ dbg_save_space_info(c);
+ c->remounting_rw = 1;
+ c->ro_mount = 0;
+
+ if (c->space_fixup) {
+ err = ubifs_fixup_free_space(c);
+ if (err)
+ goto out;
+ }
+
+ err = check_free_space(c);
+ if (err)
+ goto out;
+
+ if (c->old_leb_cnt != c->leb_cnt) {
+ struct ubifs_sb_node *sup;
+
+ sup = ubifs_read_sb_node(c);
+ if (IS_ERR(sup)) {
+ err = PTR_ERR(sup);
+ goto out;
+ }
+ sup->leb_cnt = cpu_to_le32(c->leb_cnt);
+ err = ubifs_write_sb_node(c, sup);
+ kfree(sup);
+ if (err)
+ goto out;
+ }
+
+ if (c->need_recovery) {
+ ubifs_msg(c, "completing deferred recovery");
+ err = ubifs_write_rcvrd_mst_node(c);
+ if (err)
+ goto out;
+ err = ubifs_recover_size(c);
+ if (err)
+ goto out;
+ err = ubifs_clean_lebs(c, c->sbuf);
+ if (err)
+ goto out;
+ err = ubifs_recover_inl_heads(c, c->sbuf);
+ if (err)
+ goto out;
+ } else {
+ /* A readonly mount is not allowed to have orphans */
+ ubifs_assert(c->tot_orphans == 0);
+ err = ubifs_clear_orphans(c);
+ if (err)
+ goto out;
+ }
+
+ if (!(c->mst_node->flags & cpu_to_le32(UBIFS_MST_DIRTY))) {
+ c->mst_node->flags |= cpu_to_le32(UBIFS_MST_DIRTY);
+ err = ubifs_write_master(c);
+ if (err)
+ goto out;
+ }
+
+ c->ileb_buf = vmalloc(c->leb_size);
+ if (!c->ileb_buf) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ c->write_reserve_buf = kmalloc(COMPRESSED_DATA_NODE_BUF_SZ, GFP_KERNEL);
+ if (!c->write_reserve_buf) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ err = ubifs_lpt_init(c, 0, 1);
+ if (err)
+ goto out;
+
+ /* Create background thread */
+ c->bgt = kthread_create(ubifs_bg_thread, c, "%s", c->bgt_name);
+ if (IS_ERR(c->bgt)) {
+ err = PTR_ERR(c->bgt);
+ c->bgt = NULL;
+ ubifs_err(c, "cannot spawn \"%s\", error %d",
+ c->bgt_name, err);
+ goto out;
+ }
+ wake_up_process(c->bgt);
+
+ c->orph_buf = vmalloc(c->leb_size);
+ if (!c->orph_buf) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ /* Check for enough log space */
+ lnum = c->lhead_lnum + 1;
+ if (lnum >= UBIFS_LOG_LNUM + c->log_lebs)
+ lnum = UBIFS_LOG_LNUM;
+ if (lnum == c->ltail_lnum) {
+ err = ubifs_consolidate_log(c);
+ if (err)
+ goto out;
+ }
+
+ if (c->need_recovery)
+ err = ubifs_rcvry_gc_commit(c);
+ else
+ err = ubifs_leb_unmap(c, c->gc_lnum);
+ if (err)
+ goto out;
+
+ dbg_gen("re-mounted read-write");
+ c->remounting_rw = 0;
+
+ if (c->need_recovery) {
+ c->need_recovery = 0;
+ ubifs_msg(c, "deferred recovery completed");
+ } else {
+ /*
+ * Do not run the debugging space check if the were doing
+ * recovery, because when we saved the information we had the
+ * file-system in a state where the TNC and lprops has been
+ * modified in memory, but all the I/O operations (including a
+ * commit) were deferred. So the file-system was in
+ * "non-committed" state. Now the file-system is in committed
+ * state, and of course the amount of free space will change
+ * because, for example, the old index size was imprecise.
+ */
+ err = dbg_check_space_info(c);
+ }
+
+ mutex_unlock(&c->umount_mutex);
+ return err;
+
+out:
+ c->ro_mount = 1;
+ vfree(c->orph_buf);
+ c->orph_buf = NULL;
+ if (c->bgt) {
+ kthread_stop(c->bgt);
+ c->bgt = NULL;
+ }
+ free_wbufs(c);
+ kfree(c->write_reserve_buf);
+ c->write_reserve_buf = NULL;
+ vfree(c->ileb_buf);
+ c->ileb_buf = NULL;
+ ubifs_lpt_free(c, 1);
+ c->remounting_rw = 0;
+ mutex_unlock(&c->umount_mutex);
+ return err;
+}
+
+/**
+ * ubifs_remount_ro - re-mount in read-only mode.
+ * @c: UBIFS file-system description object
+ *
+ * We assume VFS has stopped writing. Possibly the background thread could be
+ * running a commit, however kthread_stop will wait in that case.
+ */
+static void ubifs_remount_ro(struct ubifs_info *c)
+{
+ int i, err;
+
+ ubifs_assert(!c->need_recovery);
+ ubifs_assert(!c->ro_mount);
+
+ mutex_lock(&c->umount_mutex);
+ if (c->bgt) {
+ kthread_stop(c->bgt);
+ c->bgt = NULL;
+ }
+
+ dbg_save_space_info(c);
+
+ for (i = 0; i < c->jhead_cnt; i++)
+ ubifs_wbuf_sync(&c->jheads[i].wbuf);
+
+ c->mst_node->flags &= ~cpu_to_le32(UBIFS_MST_DIRTY);
+ c->mst_node->flags |= cpu_to_le32(UBIFS_MST_NO_ORPHS);
+ c->mst_node->gc_lnum = cpu_to_le32(c->gc_lnum);
+ err = ubifs_write_master(c);
+ if (err)
+ ubifs_ro_mode(c, err);
+
+ vfree(c->orph_buf);
+ c->orph_buf = NULL;
+ kfree(c->write_reserve_buf);
+ c->write_reserve_buf = NULL;
+ vfree(c->ileb_buf);
+ c->ileb_buf = NULL;
+ ubifs_lpt_free(c, 1);
+ c->ro_mount = 1;
+ err = dbg_check_space_info(c);
+ if (err)
+ ubifs_ro_mode(c, err);
+ mutex_unlock(&c->umount_mutex);
+}
+
+static void ubifs_put_super(struct super_block *sb)
+{
+ int i;
+ struct ubifs_info *c = sb->s_fs_info;
+
+ ubifs_msg(c, "un-mount UBI device %d", c->vi.ubi_num);
+
+ /*
+ * The following asserts are only valid if there has not been a failure
+ * of the media. For example, there will be dirty inodes if we failed
+ * to write them back because of I/O errors.
+ */
+ if (!c->ro_error) {
+ ubifs_assert(c->bi.idx_growth == 0);
+ ubifs_assert(c->bi.dd_growth == 0);
+ ubifs_assert(c->bi.data_growth == 0);
+ }
+
+ /*
+ * The 'c->umount_lock' prevents races between UBIFS memory shrinker
+ * and file system un-mount. Namely, it prevents the shrinker from
+ * picking this superblock for shrinking - it will be just skipped if
+ * the mutex is locked.
+ */
+ mutex_lock(&c->umount_mutex);
+ if (!c->ro_mount) {
+ /*
+ * First of all kill the background thread to make sure it does
+ * not interfere with un-mounting and freeing resources.
+ */
+ if (c->bgt) {
+ kthread_stop(c->bgt);
+ c->bgt = NULL;
+ }
+
+ /*
+ * On fatal errors c->ro_error is set to 1, in which case we do
+ * not write the master node.
+ */
+ if (!c->ro_error) {
+ int err;
+
+ /* Synchronize write-buffers */
+ for (i = 0; i < c->jhead_cnt; i++)
+ ubifs_wbuf_sync(&c->jheads[i].wbuf);
+
+ /*
+ * We are being cleanly unmounted which means the
+ * orphans were killed - indicate this in the master
+ * node. Also save the reserved GC LEB number.
+ */
+ c->mst_node->flags &= ~cpu_to_le32(UBIFS_MST_DIRTY);
+ c->mst_node->flags |= cpu_to_le32(UBIFS_MST_NO_ORPHS);
+ c->mst_node->gc_lnum = cpu_to_le32(c->gc_lnum);
+ err = ubifs_write_master(c);
+ if (err)
+ /*
+ * Recovery will attempt to fix the master area
+ * next mount, so we just print a message and
+ * continue to unmount normally.
+ */
+ ubifs_err(c, "failed to write master node, error %d",
+ err);
+ } else {
+#ifndef __UBOOT__
+ for (i = 0; i < c->jhead_cnt; i++)
+ /* Make sure write-buffer timers are canceled */
+ hrtimer_cancel(&c->jheads[i].wbuf.timer);
+#endif
+ }
+ }
+
+ ubifs_umount(c);
+#ifndef __UBOOT__
+ bdi_destroy(&c->bdi);
+#endif
+ ubi_close_volume(c->ubi);
+ mutex_unlock(&c->umount_mutex);
+}
+#endif
+
+#ifndef __UBOOT__
+static int ubifs_remount_fs(struct super_block *sb, int *flags, char *data)
+{
+ int err;
+ struct ubifs_info *c = sb->s_fs_info;
+
+ sync_filesystem(sb);
+ dbg_gen("old flags %#lx, new flags %#x", sb->s_flags, *flags);
+
+ err = ubifs_parse_options(c, data, 1);
+ if (err) {
+ ubifs_err(c, "invalid or unknown remount parameter");
+ return err;
+ }
+
+ if (c->ro_mount && !(*flags & MS_RDONLY)) {
+ if (c->ro_error) {
+ ubifs_msg(c, "cannot re-mount R/W due to prior errors");
+ return -EROFS;
+ }
+ if (c->ro_media) {
+ ubifs_msg(c, "cannot re-mount R/W - UBI volume is R/O");
+ return -EROFS;
+ }
+ err = ubifs_remount_rw(c);
+ if (err)
+ return err;
+ } else if (!c->ro_mount && (*flags & MS_RDONLY)) {
+ if (c->ro_error) {
+ ubifs_msg(c, "cannot re-mount R/O due to prior errors");
+ return -EROFS;
+ }
+ ubifs_remount_ro(c);
+ }
+
+ if (c->bulk_read == 1)
+ bu_init(c);
+ else {
+ dbg_gen("disable bulk-read");
+ kfree(c->bu.buf);
+ c->bu.buf = NULL;
+ }
+
+ ubifs_assert(c->lst.taken_empty_lebs > 0);
+ return 0;
+}
+#endif
+
+const struct super_operations ubifs_super_operations = {
+ .alloc_inode = ubifs_alloc_inode,
+#ifndef __UBOOT__
+ .destroy_inode = ubifs_destroy_inode,
+ .put_super = ubifs_put_super,
+ .write_inode = ubifs_write_inode,
+ .evict_inode = ubifs_evict_inode,
+ .statfs = ubifs_statfs,
+#endif
+ .dirty_inode = ubifs_dirty_inode,
+#ifndef __UBOOT__
+ .remount_fs = ubifs_remount_fs,
+ .show_options = ubifs_show_options,
+ .sync_fs = ubifs_sync_fs,
+#endif
+};
+
+/**
+ * open_ubi - parse UBI device name string and open the UBI device.
+ * @name: UBI volume name
+ * @mode: UBI volume open mode
+ *
+ * The primary method of mounting UBIFS is by specifying the UBI volume
+ * character device node path. However, UBIFS may also be mounted withoug any
+ * character device node using one of the following methods:
+ *
+ * o ubiX_Y - mount UBI device number X, volume Y;
+ * o ubiY - mount UBI device number 0, volume Y;
+ * o ubiX:NAME - mount UBI device X, volume with name NAME;
+ * o ubi:NAME - mount UBI device 0, volume with name NAME.
+ *
+ * Alternative '!' separator may be used instead of ':' (because some shells
+ * like busybox may interpret ':' as an NFS host name separator). This function
+ * returns UBI volume description object in case of success and a negative
+ * error code in case of failure.
+ */
+static struct ubi_volume_desc *open_ubi(const char *name, int mode)
+{
+#ifndef __UBOOT__
+ struct ubi_volume_desc *ubi;
+#endif
+ int dev, vol;
+ char *endptr;
+
+#ifndef __UBOOT__
+ /* First, try to open using the device node path method */
+ ubi = ubi_open_volume_path(name, mode);
+ if (!IS_ERR(ubi))
+ return ubi;
+#endif
+
+ /* Try the "nodev" method */
+ if (name[0] != 'u' || name[1] != 'b' || name[2] != 'i')
+ return ERR_PTR(-EINVAL);
+
+ /* ubi:NAME method */
+ if ((name[3] == ':' || name[3] == '!') && name[4] != '\0')
+ return ubi_open_volume_nm(0, name + 4, mode);
+
+ if (!isdigit(name[3]))
+ return ERR_PTR(-EINVAL);
+
+ dev = simple_strtoul(name + 3, &endptr, 0);
+
+ /* ubiY method */
+ if (*endptr == '\0')
+ return ubi_open_volume(0, dev, mode);
+
+ /* ubiX_Y method */
+ if (*endptr == '_' && isdigit(endptr[1])) {
+ vol = simple_strtoul(endptr + 1, &endptr, 0);
+ if (*endptr != '\0')
+ return ERR_PTR(-EINVAL);
+ return ubi_open_volume(dev, vol, mode);
+ }
+
+ /* ubiX:NAME method */
+ if ((*endptr == ':' || *endptr == '!') && endptr[1] != '\0')
+ return ubi_open_volume_nm(dev, ++endptr, mode);
+
+ return ERR_PTR(-EINVAL);
+}
+
+static struct ubifs_info *alloc_ubifs_info(struct ubi_volume_desc *ubi)
+{
+ struct ubifs_info *c;
+
+ c = kzalloc(sizeof(struct ubifs_info), GFP_KERNEL);
+ if (c) {
+ spin_lock_init(&c->cnt_lock);
+ spin_lock_init(&c->cs_lock);
+ spin_lock_init(&c->buds_lock);
+ spin_lock_init(&c->space_lock);
+ spin_lock_init(&c->orphan_lock);
+ init_rwsem(&c->commit_sem);
+ mutex_init(&c->lp_mutex);
+ mutex_init(&c->tnc_mutex);
+ mutex_init(&c->log_mutex);
+ mutex_init(&c->umount_mutex);
+ mutex_init(&c->bu_mutex);
+ mutex_init(&c->write_reserve_mutex);
+ init_waitqueue_head(&c->cmt_wq);
+ c->buds = RB_ROOT;
+ c->old_idx = RB_ROOT;
+ c->size_tree = RB_ROOT;
+ c->orph_tree = RB_ROOT;
+ INIT_LIST_HEAD(&c->infos_list);
+ INIT_LIST_HEAD(&c->idx_gc);
+ INIT_LIST_HEAD(&c->replay_list);
+ INIT_LIST_HEAD(&c->replay_buds);
+ INIT_LIST_HEAD(&c->uncat_list);
+ INIT_LIST_HEAD(&c->empty_list);
+ INIT_LIST_HEAD(&c->freeable_list);
+ INIT_LIST_HEAD(&c->frdi_idx_list);
+ INIT_LIST_HEAD(&c->unclean_leb_list);
+ INIT_LIST_HEAD(&c->old_buds);
+ INIT_LIST_HEAD(&c->orph_list);
+ INIT_LIST_HEAD(&c->orph_new);
+ c->no_chk_data_crc = 1;
+
+ c->highest_inum = UBIFS_FIRST_INO;
+ c->lhead_lnum = c->ltail_lnum = UBIFS_LOG_LNUM;
+
+ ubi_get_volume_info(ubi, &c->vi);
+ ubi_get_device_info(c->vi.ubi_num, &c->di);
+ }
+ return c;
+}
+
+static int ubifs_fill_super(struct super_block *sb, void *data, int silent)
+{
+ struct ubifs_info *c = sb->s_fs_info;
+ struct inode *root;
+ int err;
+
+ c->vfs_sb = sb;
+#ifndef __UBOOT__
+ /* Re-open the UBI device in read-write mode */
+ c->ubi = ubi_open_volume(c->vi.ubi_num, c->vi.vol_id, UBI_READWRITE);
+#else
+ /* U-Boot read only mode */
+ c->ubi = ubi_open_volume(c->vi.ubi_num, c->vi.vol_id, UBI_READONLY);
+#endif
+
+ if (IS_ERR(c->ubi)) {
+ err = PTR_ERR(c->ubi);
+ goto out;
+ }
+
+#ifndef __UBOOT__
+ /*
+ * UBIFS provides 'backing_dev_info' in order to disable read-ahead. For
+ * UBIFS, I/O is not deferred, it is done immediately in readpage,
+ * which means the user would have to wait not just for their own I/O
+ * but the read-ahead I/O as well i.e. completely pointless.
+ *
+ * Read-ahead will be disabled because @c->bdi.ra_pages is 0.
+ */
+ c->bdi.name = "ubifs",
+ c->bdi.capabilities = 0;
+ err = bdi_init(&c->bdi);
+ if (err)
+ goto out_close;
+ err = bdi_register(&c->bdi, NULL, "ubifs_%d_%d",
+ c->vi.ubi_num, c->vi.vol_id);
+ if (err)
+ goto out_bdi;
+
+ err = ubifs_parse_options(c, data, 0);
+ if (err)
+ goto out_bdi;
+
+ sb->s_bdi = &c->bdi;
+#endif
+ sb->s_fs_info = c;
+ sb->s_magic = UBIFS_SUPER_MAGIC;
+ sb->s_blocksize = UBIFS_BLOCK_SIZE;
+ sb->s_blocksize_bits = UBIFS_BLOCK_SHIFT;
+ sb->s_maxbytes = c->max_inode_sz = key_max_inode_size(c);
+ if (c->max_inode_sz > MAX_LFS_FILESIZE)
+ sb->s_maxbytes = c->max_inode_sz = MAX_LFS_FILESIZE;
+ sb->s_op = &ubifs_super_operations;
+#ifndef __UBOOT__
+ sb->s_xattr = ubifs_xattr_handlers;
+#endif
+
+ mutex_lock(&c->umount_mutex);
+ err = mount_ubifs(c);
+ if (err) {
+ ubifs_assert(err < 0);
+ goto out_unlock;
+ }
+
+ /* Read the root inode */
+ root = ubifs_iget(sb, UBIFS_ROOT_INO);
+ if (IS_ERR(root)) {
+ err = PTR_ERR(root);
+ goto out_umount;
+ }
+
+#ifndef __UBOOT__
+ sb->s_root = d_make_root(root);
+ if (!sb->s_root) {
+ err = -ENOMEM;
+ goto out_umount;
+ }
+#else
+ sb->s_root = NULL;
+#endif
+
+ mutex_unlock(&c->umount_mutex);
+ return 0;
+
+out_umount:
+ ubifs_umount(c);
+out_unlock:
+ mutex_unlock(&c->umount_mutex);
+#ifndef __UBOOT__
+out_bdi:
+ bdi_destroy(&c->bdi);
+out_close:
+#endif
+ ubi_close_volume(c->ubi);
+out:
+ return err;
+}
+
+static int sb_test(struct super_block *sb, void *data)
+{
+ struct ubifs_info *c1 = data;
+ struct ubifs_info *c = sb->s_fs_info;
+
+ return c->vi.cdev == c1->vi.cdev;
+}
+
+static int sb_set(struct super_block *sb, void *data)
+{
+ sb->s_fs_info = data;
+ return set_anon_super(sb, NULL);
+}
+
+static struct super_block *alloc_super(struct file_system_type *type, int flags)
+{
+ struct super_block *s;
+ int err;
+
+ s = kzalloc(sizeof(struct super_block), GFP_USER);
+ if (!s) {
+ err = -ENOMEM;
+ return ERR_PTR(err);
+ }
+
+#ifndef __UBOOT__
+ INIT_HLIST_NODE(&s->s_instances);
+#endif
+ INIT_LIST_HEAD(&s->s_inodes);
+ s->s_time_gran = 1000000000;
+ s->s_flags = flags;
+
+ return s;
+}
+
+/**
+ * sget - find or create a superblock
+ * @type: filesystem type superblock should belong to
+ * @test: comparison callback
+ * @set: setup callback
+ * @flags: mount flags
+ * @data: argument to each of them
+ */
+struct super_block *sget(struct file_system_type *type,
+ int (*test)(struct super_block *,void *),
+ int (*set)(struct super_block *,void *),
+ int flags,
+ void *data)
+{
+ struct super_block *s = NULL;
+#ifndef __UBOOT__
+ struct super_block *old;
+#endif
+ int err;
+
+#ifndef __UBOOT__
+retry:
+ spin_lock(&sb_lock);
+ if (test) {
+ hlist_for_each_entry(old, &type->fs_supers, s_instances) {
+ if (!test(old, data))
+ continue;
+ if (!grab_super(old))
+ goto retry;
+ if (s) {
+ up_write(&s->s_umount);
+ destroy_super(s);
+ s = NULL;
+ }
+ return old;
+ }
+ }
+#endif
+ if (!s) {
+ spin_unlock(&sb_lock);
+ s = alloc_super(type, flags);
+ if (!s)
+ return ERR_PTR(-ENOMEM);
+#ifndef __UBOOT__
+ goto retry;
+#endif
+ }
+
+ err = set(s, data);
+ if (err) {
+#ifndef __UBOOT__
+ spin_unlock(&sb_lock);
+ up_write(&s->s_umount);
+ destroy_super(s);
+#endif
+ return ERR_PTR(err);
+ }
+ s->s_type = type;
+#ifndef __UBOOT__
+ strlcpy(s->s_id, type->name, sizeof(s->s_id));
+ list_add_tail(&s->s_list, &super_blocks);
+ hlist_add_head(&s->s_instances, &type->fs_supers);
+ spin_unlock(&sb_lock);
+ get_filesystem(type);
+ register_shrinker(&s->s_shrink);
+#else
+ strncpy(s->s_id, type->name, sizeof(s->s_id));
+#endif
+ return s;
+}
+
+EXPORT_SYMBOL(sget);
+
+
+static struct dentry *ubifs_mount(struct file_system_type *fs_type, int flags,
+ const char *name, void *data)
+{
+ struct ubi_volume_desc *ubi;
+ struct ubifs_info *c;
+ struct super_block *sb;
+ int err;
+
+ dbg_gen("name %s, flags %#x", name, flags);
+
+ /*
+ * Get UBI device number and volume ID. Mount it read-only so far
+ * because this might be a new mount point, and UBI allows only one
+ * read-write user at a time.
+ */
+ ubi = open_ubi(name, UBI_READONLY);
+ if (IS_ERR(ubi)) {
+ pr_err("UBIFS error (pid: %d): cannot open \"%s\", error %d\n",
+ current->pid, name, (int)PTR_ERR(ubi));
+ return ERR_CAST(ubi);
+ }
+
+ c = alloc_ubifs_info(ubi);
+ if (!c) {
+ err = -ENOMEM;
+ goto out_close;
+ }
+
+ dbg_gen("opened ubi%d_%d", c->vi.ubi_num, c->vi.vol_id);
+
+ sb = sget(fs_type, sb_test, sb_set, flags, c);
+ if (IS_ERR(sb)) {
+ err = PTR_ERR(sb);
+ kfree(c);
+ goto out_close;
+ }
+
+ if (sb->s_root) {
+ struct ubifs_info *c1 = sb->s_fs_info;
+ kfree(c);
+ /* A new mount point for already mounted UBIFS */
+ dbg_gen("this ubi volume is already mounted");
+ if (!!(flags & MS_RDONLY) != c1->ro_mount) {
+ err = -EBUSY;
+ goto out_deact;
+ }
+ } else {
+ err = ubifs_fill_super(sb, data, flags & MS_SILENT ? 1 : 0);
+ if (err)
+ goto out_deact;
+ /* We do not support atime */
+ sb->s_flags |= MS_ACTIVE | MS_NOATIME;
+ }
+
+ /* 'fill_super()' opens ubi again so we must close it here */
+ ubi_close_volume(ubi);
+
+#ifdef __UBOOT__
+ ubifs_sb = sb;
+ return 0;
+#else
+ return dget(sb->s_root);
+#endif
+
+out_deact:
+#ifndef __UBOOT__
+ deactivate_locked_super(sb);
+#endif
+out_close:
+ ubi_close_volume(ubi);
+ return ERR_PTR(err);
+}
+
+static void kill_ubifs_super(struct super_block *s)
+{
+ struct ubifs_info *c = s->s_fs_info;
+#ifndef __UBOOT__
+ kill_anon_super(s);
+#endif
+ kfree(c);
+}
+
+static struct file_system_type ubifs_fs_type = {
+ .name = "ubifs",
+ .owner = THIS_MODULE,
+ .mount = ubifs_mount,
+ .kill_sb = kill_ubifs_super,
+};
+#ifndef __UBOOT__
+MODULE_ALIAS_FS("ubifs");
+
+/*
+ * Inode slab cache constructor.
+ */
+static void inode_slab_ctor(void *obj)
+{
+ struct ubifs_inode *ui = obj;
+ inode_init_once(&ui->vfs_inode);
+}
+
+static int __init ubifs_init(void)
+#else
+int ubifs_init(void)
+#endif
+{
+ int err;
+
+ BUILD_BUG_ON(sizeof(struct ubifs_ch) != 24);
+
+ /* Make sure node sizes are 8-byte aligned */
+ BUILD_BUG_ON(UBIFS_CH_SZ & 7);
+ BUILD_BUG_ON(UBIFS_INO_NODE_SZ & 7);
+ BUILD_BUG_ON(UBIFS_DENT_NODE_SZ & 7);
+ BUILD_BUG_ON(UBIFS_XENT_NODE_SZ & 7);
+ BUILD_BUG_ON(UBIFS_DATA_NODE_SZ & 7);
+ BUILD_BUG_ON(UBIFS_TRUN_NODE_SZ & 7);
+ BUILD_BUG_ON(UBIFS_SB_NODE_SZ & 7);
+ BUILD_BUG_ON(UBIFS_MST_NODE_SZ & 7);
+ BUILD_BUG_ON(UBIFS_REF_NODE_SZ & 7);
+ BUILD_BUG_ON(UBIFS_CS_NODE_SZ & 7);
+ BUILD_BUG_ON(UBIFS_ORPH_NODE_SZ & 7);
+
+ BUILD_BUG_ON(UBIFS_MAX_DENT_NODE_SZ & 7);
+ BUILD_BUG_ON(UBIFS_MAX_XENT_NODE_SZ & 7);
+ BUILD_BUG_ON(UBIFS_MAX_DATA_NODE_SZ & 7);
+ BUILD_BUG_ON(UBIFS_MAX_INO_NODE_SZ & 7);
+ BUILD_BUG_ON(UBIFS_MAX_NODE_SZ & 7);
+ BUILD_BUG_ON(MIN_WRITE_SZ & 7);
+
+ /* Check min. node size */
+ BUILD_BUG_ON(UBIFS_INO_NODE_SZ < MIN_WRITE_SZ);
+ BUILD_BUG_ON(UBIFS_DENT_NODE_SZ < MIN_WRITE_SZ);
+ BUILD_BUG_ON(UBIFS_XENT_NODE_SZ < MIN_WRITE_SZ);
+ BUILD_BUG_ON(UBIFS_TRUN_NODE_SZ < MIN_WRITE_SZ);
+
+ BUILD_BUG_ON(UBIFS_MAX_DENT_NODE_SZ > UBIFS_MAX_NODE_SZ);
+ BUILD_BUG_ON(UBIFS_MAX_XENT_NODE_SZ > UBIFS_MAX_NODE_SZ);
+ BUILD_BUG_ON(UBIFS_MAX_DATA_NODE_SZ > UBIFS_MAX_NODE_SZ);
+ BUILD_BUG_ON(UBIFS_MAX_INO_NODE_SZ > UBIFS_MAX_NODE_SZ);
+
+ /* Defined node sizes */
+ BUILD_BUG_ON(UBIFS_SB_NODE_SZ != 4096);
+ BUILD_BUG_ON(UBIFS_MST_NODE_SZ != 512);
+ BUILD_BUG_ON(UBIFS_INO_NODE_SZ != 160);
+ BUILD_BUG_ON(UBIFS_REF_NODE_SZ != 64);
+
+ /*
+ * We use 2 bit wide bit-fields to store compression type, which should
+ * be amended if more compressors are added. The bit-fields are:
+ * @compr_type in 'struct ubifs_inode', @default_compr in
+ * 'struct ubifs_info' and @compr_type in 'struct ubifs_mount_opts'.
+ */
+ BUILD_BUG_ON(UBIFS_COMPR_TYPES_CNT > 4);
+
+ /*
+ * We require that PAGE_CACHE_SIZE is greater-than-or-equal-to
+ * UBIFS_BLOCK_SIZE. It is assumed that both are powers of 2.
+ */
+ if (PAGE_CACHE_SIZE < UBIFS_BLOCK_SIZE) {
+ pr_err("UBIFS error (pid %d): VFS page cache size is %u bytes, but UBIFS requires at least 4096 bytes\n",
+ current->pid, (unsigned int)PAGE_CACHE_SIZE);
+ return -EINVAL;
+ }
+
+#ifndef __UBOOT__
+ ubifs_inode_slab = kmem_cache_create("ubifs_inode_slab",
+ sizeof(struct ubifs_inode), 0,
+ SLAB_MEM_SPREAD | SLAB_RECLAIM_ACCOUNT,
+ &inode_slab_ctor);
+ if (!ubifs_inode_slab)
+ return -ENOMEM;
+
+ err = register_shrinker(&ubifs_shrinker_info);
+ if (err)
+ goto out_slab;
+#endif
+
+ err = ubifs_compressors_init();
+ if (err)
+ goto out_shrinker;
+
+#ifndef __UBOOT__
+ err = dbg_debugfs_init();
+ if (err)
+ goto out_compr;
+
+ err = register_filesystem(&ubifs_fs_type);
+ if (err) {
+ pr_err("UBIFS error (pid %d): cannot register file system, error %d\n",
+ current->pid, err);
+ goto out_dbg;
+ }
+#endif
+ return 0;
+
+#ifndef __UBOOT__
+out_dbg:
+ dbg_debugfs_exit();
+out_compr:
+ ubifs_compressors_exit();
+#endif
+out_shrinker:
+#ifndef __UBOOT__
+ unregister_shrinker(&ubifs_shrinker_info);
+out_slab:
+#endif
+ kmem_cache_destroy(ubifs_inode_slab);
+ return err;
+}
+/* late_initcall to let compressors initialize first */
+late_initcall(ubifs_init);
+
+#ifndef __UBOOT__
+static void __exit ubifs_exit(void)
+{
+ ubifs_assert(list_empty(&ubifs_infos));
+ ubifs_assert(atomic_long_read(&ubifs_clean_zn_cnt) == 0);
+
+ dbg_debugfs_exit();
+ ubifs_compressors_exit();
+ unregister_shrinker(&ubifs_shrinker_info);
+
+ /*
+ * Make sure all delayed rcu free inodes are flushed before we
+ * destroy cache.
+ */
+ rcu_barrier();
+ kmem_cache_destroy(ubifs_inode_slab);
+ unregister_filesystem(&ubifs_fs_type);
+}
+module_exit(ubifs_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_VERSION(__stringify(UBIFS_VERSION));
+MODULE_AUTHOR("Artem Bityutskiy, Adrian Hunter");
+MODULE_DESCRIPTION("UBIFS - UBI File System");
+#else
+int uboot_ubifs_mount(char *vol_name)
+{
+ struct dentry *ret;
+ int flags;
+
+ /*
+ * First unmount if allready mounted
+ */
+ if (ubifs_sb)
+ ubifs_umount(ubifs_sb->s_fs_info);
+
+ /*
+ * Mount in read-only mode
+ */
+ flags = MS_RDONLY;
+ ret = ubifs_mount(&ubifs_fs_type, flags, vol_name, NULL);
+ if (IS_ERR(ret)) {
+ printf("Error reading superblock on volume '%s' " \
+ "errno=%d!\n", vol_name, (int)PTR_ERR(ret));
+ return -1;
+ }
+
+ return 0;
+}
+#endif
diff --git a/roms/u-boot/fs/ubifs/tnc.c b/roms/u-boot/fs/ubifs/tnc.c
new file mode 100644
index 000000000..e8b8f1683
--- /dev/null
+++ b/roms/u-boot/fs/ubifs/tnc.c
@@ -0,0 +1,3330 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ *
+ * Authors: Adrian Hunter
+ * Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * This file implements TNC (Tree Node Cache) which caches indexing nodes of
+ * the UBIFS B-tree.
+ *
+ * At the moment the locking rules of the TNC tree are quite simple and
+ * straightforward. We just have a mutex and lock it when we traverse the
+ * tree. If a znode is not in memory, we read it from flash while still having
+ * the mutex locked.
+ */
+
+#ifndef __UBOOT__
+#include <log.h>
+#include <dm/devres.h>
+#include <linux/crc32.h>
+#include <linux/slab.h>
+#include <u-boot/crc.h>
+#else
+#include <linux/bitops.h>
+#include <linux/bug.h>
+#include <linux/compat.h>
+#include <linux/err.h>
+#include <linux/stat.h>
+#endif
+#include "ubifs.h"
+
+/*
+ * Returned codes of 'matches_name()' and 'fallible_matches_name()' functions.
+ * @NAME_LESS: name corresponding to the first argument is less than second
+ * @NAME_MATCHES: names match
+ * @NAME_GREATER: name corresponding to the second argument is greater than
+ * first
+ * @NOT_ON_MEDIA: node referred by zbranch does not exist on the media
+ *
+ * These constants were introduce to improve readability.
+ */
+enum {
+ NAME_LESS = 0,
+ NAME_MATCHES = 1,
+ NAME_GREATER = 2,
+ NOT_ON_MEDIA = 3,
+};
+
+/**
+ * insert_old_idx - record an index node obsoleted since the last commit start.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number of obsoleted index node
+ * @offs: offset of obsoleted index node
+ *
+ * Returns %0 on success, and a negative error code on failure.
+ *
+ * For recovery, there must always be a complete intact version of the index on
+ * flash at all times. That is called the "old index". It is the index as at the
+ * time of the last successful commit. Many of the index nodes in the old index
+ * may be dirty, but they must not be erased until the next successful commit
+ * (at which point that index becomes the old index).
+ *
+ * That means that the garbage collection and the in-the-gaps method of
+ * committing must be able to determine if an index node is in the old index.
+ * Most of the old index nodes can be found by looking up the TNC using the
+ * 'lookup_znode()' function. However, some of the old index nodes may have
+ * been deleted from the current index or may have been changed so much that
+ * they cannot be easily found. In those cases, an entry is added to an RB-tree.
+ * That is what this function does. The RB-tree is ordered by LEB number and
+ * offset because they uniquely identify the old index node.
+ */
+static int insert_old_idx(struct ubifs_info *c, int lnum, int offs)
+{
+ struct ubifs_old_idx *old_idx, *o;
+ struct rb_node **p, *parent = NULL;
+
+ old_idx = kmalloc(sizeof(struct ubifs_old_idx), GFP_NOFS);
+ if (unlikely(!old_idx))
+ return -ENOMEM;
+ old_idx->lnum = lnum;
+ old_idx->offs = offs;
+
+ p = &c->old_idx.rb_node;
+ while (*p) {
+ parent = *p;
+ o = rb_entry(parent, struct ubifs_old_idx, rb);
+ if (lnum < o->lnum)
+ p = &(*p)->rb_left;
+ else if (lnum > o->lnum)
+ p = &(*p)->rb_right;
+ else if (offs < o->offs)
+ p = &(*p)->rb_left;
+ else if (offs > o->offs)
+ p = &(*p)->rb_right;
+ else {
+ ubifs_err(c, "old idx added twice!");
+ kfree(old_idx);
+ return 0;
+ }
+ }
+ rb_link_node(&old_idx->rb, parent, p);
+ rb_insert_color(&old_idx->rb, &c->old_idx);
+ return 0;
+}
+
+/**
+ * insert_old_idx_znode - record a znode obsoleted since last commit start.
+ * @c: UBIFS file-system description object
+ * @znode: znode of obsoleted index node
+ *
+ * Returns %0 on success, and a negative error code on failure.
+ */
+int insert_old_idx_znode(struct ubifs_info *c, struct ubifs_znode *znode)
+{
+ if (znode->parent) {
+ struct ubifs_zbranch *zbr;
+
+ zbr = &znode->parent->zbranch[znode->iip];
+ if (zbr->len)
+ return insert_old_idx(c, zbr->lnum, zbr->offs);
+ } else
+ if (c->zroot.len)
+ return insert_old_idx(c, c->zroot.lnum,
+ c->zroot.offs);
+ return 0;
+}
+
+/**
+ * ins_clr_old_idx_znode - record a znode obsoleted since last commit start.
+ * @c: UBIFS file-system description object
+ * @znode: znode of obsoleted index node
+ *
+ * Returns %0 on success, and a negative error code on failure.
+ */
+static int ins_clr_old_idx_znode(struct ubifs_info *c,
+ struct ubifs_znode *znode)
+{
+ int err;
+
+ if (znode->parent) {
+ struct ubifs_zbranch *zbr;
+
+ zbr = &znode->parent->zbranch[znode->iip];
+ if (zbr->len) {
+ err = insert_old_idx(c, zbr->lnum, zbr->offs);
+ if (err)
+ return err;
+ zbr->lnum = 0;
+ zbr->offs = 0;
+ zbr->len = 0;
+ }
+ } else
+ if (c->zroot.len) {
+ err = insert_old_idx(c, c->zroot.lnum, c->zroot.offs);
+ if (err)
+ return err;
+ c->zroot.lnum = 0;
+ c->zroot.offs = 0;
+ c->zroot.len = 0;
+ }
+ return 0;
+}
+
+/**
+ * destroy_old_idx - destroy the old_idx RB-tree.
+ * @c: UBIFS file-system description object
+ *
+ * During start commit, the old_idx RB-tree is used to avoid overwriting index
+ * nodes that were in the index last commit but have since been deleted. This
+ * is necessary for recovery i.e. the old index must be kept intact until the
+ * new index is successfully written. The old-idx RB-tree is used for the
+ * in-the-gaps method of writing index nodes and is destroyed every commit.
+ */
+void destroy_old_idx(struct ubifs_info *c)
+{
+ struct ubifs_old_idx *old_idx, *n;
+
+ rbtree_postorder_for_each_entry_safe(old_idx, n, &c->old_idx, rb)
+ kfree(old_idx);
+
+ c->old_idx = RB_ROOT;
+}
+
+/**
+ * copy_znode - copy a dirty znode.
+ * @c: UBIFS file-system description object
+ * @znode: znode to copy
+ *
+ * A dirty znode being committed may not be changed, so it is copied.
+ */
+static struct ubifs_znode *copy_znode(struct ubifs_info *c,
+ struct ubifs_znode *znode)
+{
+ struct ubifs_znode *zn;
+
+ zn = kmalloc(c->max_znode_sz, GFP_NOFS);
+ if (unlikely(!zn))
+ return ERR_PTR(-ENOMEM);
+
+ memcpy(zn, znode, c->max_znode_sz);
+ zn->cnext = NULL;
+ __set_bit(DIRTY_ZNODE, &zn->flags);
+ __clear_bit(COW_ZNODE, &zn->flags);
+
+ ubifs_assert(!ubifs_zn_obsolete(znode));
+ __set_bit(OBSOLETE_ZNODE, &znode->flags);
+
+ if (znode->level != 0) {
+ int i;
+ const int n = zn->child_cnt;
+
+ /* The children now have new parent */
+ for (i = 0; i < n; i++) {
+ struct ubifs_zbranch *zbr = &zn->zbranch[i];
+
+ if (zbr->znode)
+ zbr->znode->parent = zn;
+ }
+ }
+
+ atomic_long_inc(&c->dirty_zn_cnt);
+ return zn;
+}
+
+/**
+ * add_idx_dirt - add dirt due to a dirty znode.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number of index node
+ * @dirt: size of index node
+ *
+ * This function updates lprops dirty space and the new size of the index.
+ */
+static int add_idx_dirt(struct ubifs_info *c, int lnum, int dirt)
+{
+ c->calc_idx_sz -= ALIGN(dirt, 8);
+ return ubifs_add_dirt(c, lnum, dirt);
+}
+
+/**
+ * dirty_cow_znode - ensure a znode is not being committed.
+ * @c: UBIFS file-system description object
+ * @zbr: branch of znode to check
+ *
+ * Returns dirtied znode on success or negative error code on failure.
+ */
+static struct ubifs_znode *dirty_cow_znode(struct ubifs_info *c,
+ struct ubifs_zbranch *zbr)
+{
+ struct ubifs_znode *znode = zbr->znode;
+ struct ubifs_znode *zn;
+ int err;
+
+ if (!ubifs_zn_cow(znode)) {
+ /* znode is not being committed */
+ if (!test_and_set_bit(DIRTY_ZNODE, &znode->flags)) {
+ atomic_long_inc(&c->dirty_zn_cnt);
+ atomic_long_dec(&c->clean_zn_cnt);
+ atomic_long_dec(&ubifs_clean_zn_cnt);
+ err = add_idx_dirt(c, zbr->lnum, zbr->len);
+ if (unlikely(err))
+ return ERR_PTR(err);
+ }
+ return znode;
+ }
+
+ zn = copy_znode(c, znode);
+ if (IS_ERR(zn))
+ return zn;
+
+ if (zbr->len) {
+ err = insert_old_idx(c, zbr->lnum, zbr->offs);
+ if (unlikely(err))
+ return ERR_PTR(err);
+ err = add_idx_dirt(c, zbr->lnum, zbr->len);
+ } else
+ err = 0;
+
+ zbr->znode = zn;
+ zbr->lnum = 0;
+ zbr->offs = 0;
+ zbr->len = 0;
+
+ if (unlikely(err))
+ return ERR_PTR(err);
+ return zn;
+}
+
+/**
+ * lnc_add - add a leaf node to the leaf node cache.
+ * @c: UBIFS file-system description object
+ * @zbr: zbranch of leaf node
+ * @node: leaf node
+ *
+ * Leaf nodes are non-index nodes directory entry nodes or data nodes. The
+ * purpose of the leaf node cache is to save re-reading the same leaf node over
+ * and over again. Most things are cached by VFS, however the file system must
+ * cache directory entries for readdir and for resolving hash collisions. The
+ * present implementation of the leaf node cache is extremely simple, and
+ * allows for error returns that are not used but that may be needed if a more
+ * complex implementation is created.
+ *
+ * Note, this function does not add the @node object to LNC directly, but
+ * allocates a copy of the object and adds the copy to LNC. The reason for this
+ * is that @node has been allocated outside of the TNC subsystem and will be
+ * used with @c->tnc_mutex unlock upon return from the TNC subsystem. But LNC
+ * may be changed at any time, e.g. freed by the shrinker.
+ */
+static int lnc_add(struct ubifs_info *c, struct ubifs_zbranch *zbr,
+ const void *node)
+{
+ int err;
+ void *lnc_node;
+ const struct ubifs_dent_node *dent = node;
+
+ ubifs_assert(!zbr->leaf);
+ ubifs_assert(zbr->len != 0);
+ ubifs_assert(is_hash_key(c, &zbr->key));
+
+ err = ubifs_validate_entry(c, dent);
+ if (err) {
+ dump_stack();
+ ubifs_dump_node(c, dent);
+ return err;
+ }
+
+ lnc_node = kmemdup(node, zbr->len, GFP_NOFS);
+ if (!lnc_node)
+ /* We don't have to have the cache, so no error */
+ return 0;
+
+ zbr->leaf = lnc_node;
+ return 0;
+}
+
+ /**
+ * lnc_add_directly - add a leaf node to the leaf-node-cache.
+ * @c: UBIFS file-system description object
+ * @zbr: zbranch of leaf node
+ * @node: leaf node
+ *
+ * This function is similar to 'lnc_add()', but it does not create a copy of
+ * @node but inserts @node to TNC directly.
+ */
+static int lnc_add_directly(struct ubifs_info *c, struct ubifs_zbranch *zbr,
+ void *node)
+{
+ int err;
+
+ ubifs_assert(!zbr->leaf);
+ ubifs_assert(zbr->len != 0);
+
+ err = ubifs_validate_entry(c, node);
+ if (err) {
+ dump_stack();
+ ubifs_dump_node(c, node);
+ return err;
+ }
+
+ zbr->leaf = node;
+ return 0;
+}
+
+/**
+ * lnc_free - remove a leaf node from the leaf node cache.
+ * @zbr: zbranch of leaf node
+ * @node: leaf node
+ */
+static void lnc_free(struct ubifs_zbranch *zbr)
+{
+ if (!zbr->leaf)
+ return;
+ kfree(zbr->leaf);
+ zbr->leaf = NULL;
+}
+
+/**
+ * tnc_read_node_nm - read a "hashed" leaf node.
+ * @c: UBIFS file-system description object
+ * @zbr: key and position of the node
+ * @node: node is returned here
+ *
+ * This function reads a "hashed" node defined by @zbr from the leaf node cache
+ * (in it is there) or from the hash media, in which case the node is also
+ * added to LNC. Returns zero in case of success or a negative negative error
+ * code in case of failure.
+ */
+static int tnc_read_node_nm(struct ubifs_info *c, struct ubifs_zbranch *zbr,
+ void *node)
+{
+ int err;
+
+ ubifs_assert(is_hash_key(c, &zbr->key));
+
+ if (zbr->leaf) {
+ /* Read from the leaf node cache */
+ ubifs_assert(zbr->len != 0);
+ memcpy(node, zbr->leaf, zbr->len);
+ return 0;
+ }
+
+ err = ubifs_tnc_read_node(c, zbr, node);
+ if (err)
+ return err;
+
+ /* Add the node to the leaf node cache */
+ err = lnc_add(c, zbr, node);
+ return err;
+}
+
+/**
+ * try_read_node - read a node if it is a node.
+ * @c: UBIFS file-system description object
+ * @buf: buffer to read to
+ * @type: node type
+ * @len: node length (not aligned)
+ * @lnum: LEB number of node to read
+ * @offs: offset of node to read
+ *
+ * This function tries to read a node of known type and length, checks it and
+ * stores it in @buf. This function returns %1 if a node is present and %0 if
+ * a node is not present. A negative error code is returned for I/O errors.
+ * This function performs that same function as ubifs_read_node except that
+ * it does not require that there is actually a node present and instead
+ * the return code indicates if a node was read.
+ *
+ * Note, this function does not check CRC of data nodes if @c->no_chk_data_crc
+ * is true (it is controlled by corresponding mount option). However, if
+ * @c->mounting or @c->remounting_rw is true (we are mounting or re-mounting to
+ * R/W mode), @c->no_chk_data_crc is ignored and CRC is checked. This is
+ * because during mounting or re-mounting from R/O mode to R/W mode we may read
+ * journal nodes (when replying the journal or doing the recovery) and the
+ * journal nodes may potentially be corrupted, so checking is required.
+ */
+static int try_read_node(const struct ubifs_info *c, void *buf, int type,
+ int len, int lnum, int offs)
+{
+ int err, node_len;
+ struct ubifs_ch *ch = buf;
+ uint32_t crc, node_crc;
+
+ dbg_io("LEB %d:%d, %s, length %d", lnum, offs, dbg_ntype(type), len);
+
+ err = ubifs_leb_read(c, lnum, buf, offs, len, 1);
+ if (err) {
+ ubifs_err(c, "cannot read node type %d from LEB %d:%d, error %d",
+ type, lnum, offs, err);
+ return err;
+ }
+
+ if (le32_to_cpu(ch->magic) != UBIFS_NODE_MAGIC)
+ return 0;
+
+ if (ch->node_type != type)
+ return 0;
+
+ node_len = le32_to_cpu(ch->len);
+ if (node_len != len)
+ return 0;
+
+ if (type == UBIFS_DATA_NODE && c->no_chk_data_crc && !c->mounting &&
+ !c->remounting_rw)
+ return 1;
+
+ crc = crc32(UBIFS_CRC32_INIT, buf + 8, node_len - 8);
+ node_crc = le32_to_cpu(ch->crc);
+ if (crc != node_crc)
+ return 0;
+
+ return 1;
+}
+
+/**
+ * fallible_read_node - try to read a leaf node.
+ * @c: UBIFS file-system description object
+ * @key: key of node to read
+ * @zbr: position of node
+ * @node: node returned
+ *
+ * This function tries to read a node and returns %1 if the node is read, %0
+ * if the node is not present, and a negative error code in the case of error.
+ */
+static int fallible_read_node(struct ubifs_info *c, const union ubifs_key *key,
+ struct ubifs_zbranch *zbr, void *node)
+{
+ int ret;
+
+ dbg_tnck(key, "LEB %d:%d, key ", zbr->lnum, zbr->offs);
+
+ ret = try_read_node(c, node, key_type(c, key), zbr->len, zbr->lnum,
+ zbr->offs);
+ if (ret == 1) {
+ union ubifs_key node_key;
+ struct ubifs_dent_node *dent = node;
+
+ /* All nodes have key in the same place */
+ key_read(c, &dent->key, &node_key);
+ if (keys_cmp(c, key, &node_key) != 0)
+ ret = 0;
+ }
+ if (ret == 0 && c->replaying)
+ dbg_mntk(key, "dangling branch LEB %d:%d len %d, key ",
+ zbr->lnum, zbr->offs, zbr->len);
+ return ret;
+}
+
+/**
+ * matches_name - determine if a direntry or xattr entry matches a given name.
+ * @c: UBIFS file-system description object
+ * @zbr: zbranch of dent
+ * @nm: name to match
+ *
+ * This function checks if xentry/direntry referred by zbranch @zbr matches name
+ * @nm. Returns %NAME_MATCHES if it does, %NAME_LESS if the name referred by
+ * @zbr is less than @nm, and %NAME_GREATER if it is greater than @nm. In case
+ * of failure, a negative error code is returned.
+ */
+static int matches_name(struct ubifs_info *c, struct ubifs_zbranch *zbr,
+ const struct qstr *nm)
+{
+ struct ubifs_dent_node *dent;
+ int nlen, err;
+
+ /* If possible, match against the dent in the leaf node cache */
+ if (!zbr->leaf) {
+ dent = kmalloc(zbr->len, GFP_NOFS);
+ if (!dent)
+ return -ENOMEM;
+
+ err = ubifs_tnc_read_node(c, zbr, dent);
+ if (err)
+ goto out_free;
+
+ /* Add the node to the leaf node cache */
+ err = lnc_add_directly(c, zbr, dent);
+ if (err)
+ goto out_free;
+ } else
+ dent = zbr->leaf;
+
+ nlen = le16_to_cpu(dent->nlen);
+ err = memcmp(dent->name, nm->name, min_t(int, nlen, nm->len));
+ if (err == 0) {
+ if (nlen == nm->len)
+ return NAME_MATCHES;
+ else if (nlen < nm->len)
+ return NAME_LESS;
+ else
+ return NAME_GREATER;
+ } else if (err < 0)
+ return NAME_LESS;
+ else
+ return NAME_GREATER;
+
+out_free:
+ kfree(dent);
+ return err;
+}
+
+/**
+ * get_znode - get a TNC znode that may not be loaded yet.
+ * @c: UBIFS file-system description object
+ * @znode: parent znode
+ * @n: znode branch slot number
+ *
+ * This function returns the znode or a negative error code.
+ */
+static struct ubifs_znode *get_znode(struct ubifs_info *c,
+ struct ubifs_znode *znode, int n)
+{
+ struct ubifs_zbranch *zbr;
+
+ zbr = &znode->zbranch[n];
+ if (zbr->znode)
+ znode = zbr->znode;
+ else
+ znode = ubifs_load_znode(c, zbr, znode, n);
+ return znode;
+}
+
+/**
+ * tnc_next - find next TNC entry.
+ * @c: UBIFS file-system description object
+ * @zn: znode is passed and returned here
+ * @n: znode branch slot number is passed and returned here
+ *
+ * This function returns %0 if the next TNC entry is found, %-ENOENT if there is
+ * no next entry, or a negative error code otherwise.
+ */
+static int tnc_next(struct ubifs_info *c, struct ubifs_znode **zn, int *n)
+{
+ struct ubifs_znode *znode = *zn;
+ int nn = *n;
+
+ nn += 1;
+ if (nn < znode->child_cnt) {
+ *n = nn;
+ return 0;
+ }
+ while (1) {
+ struct ubifs_znode *zp;
+
+ zp = znode->parent;
+ if (!zp)
+ return -ENOENT;
+ nn = znode->iip + 1;
+ znode = zp;
+ if (nn < znode->child_cnt) {
+ znode = get_znode(c, znode, nn);
+ if (IS_ERR(znode))
+ return PTR_ERR(znode);
+ while (znode->level != 0) {
+ znode = get_znode(c, znode, 0);
+ if (IS_ERR(znode))
+ return PTR_ERR(znode);
+ }
+ nn = 0;
+ break;
+ }
+ }
+ *zn = znode;
+ *n = nn;
+ return 0;
+}
+
+/**
+ * tnc_prev - find previous TNC entry.
+ * @c: UBIFS file-system description object
+ * @zn: znode is returned here
+ * @n: znode branch slot number is passed and returned here
+ *
+ * This function returns %0 if the previous TNC entry is found, %-ENOENT if
+ * there is no next entry, or a negative error code otherwise.
+ */
+static int tnc_prev(struct ubifs_info *c, struct ubifs_znode **zn, int *n)
+{
+ struct ubifs_znode *znode = *zn;
+ int nn = *n;
+
+ if (nn > 0) {
+ *n = nn - 1;
+ return 0;
+ }
+ while (1) {
+ struct ubifs_znode *zp;
+
+ zp = znode->parent;
+ if (!zp)
+ return -ENOENT;
+ nn = znode->iip - 1;
+ znode = zp;
+ if (nn >= 0) {
+ znode = get_znode(c, znode, nn);
+ if (IS_ERR(znode))
+ return PTR_ERR(znode);
+ while (znode->level != 0) {
+ nn = znode->child_cnt - 1;
+ znode = get_znode(c, znode, nn);
+ if (IS_ERR(znode))
+ return PTR_ERR(znode);
+ }
+ nn = znode->child_cnt - 1;
+ break;
+ }
+ }
+ *zn = znode;
+ *n = nn;
+ return 0;
+}
+
+/**
+ * resolve_collision - resolve a collision.
+ * @c: UBIFS file-system description object
+ * @key: key of a directory or extended attribute entry
+ * @zn: znode is returned here
+ * @n: zbranch number is passed and returned here
+ * @nm: name of the entry
+ *
+ * This function is called for "hashed" keys to make sure that the found key
+ * really corresponds to the looked up node (directory or extended attribute
+ * entry). It returns %1 and sets @zn and @n if the collision is resolved.
+ * %0 is returned if @nm is not found and @zn and @n are set to the previous
+ * entry, i.e. to the entry after which @nm could follow if it were in TNC.
+ * This means that @n may be set to %-1 if the leftmost key in @zn is the
+ * previous one. A negative error code is returned on failures.
+ */
+static int resolve_collision(struct ubifs_info *c, const union ubifs_key *key,
+ struct ubifs_znode **zn, int *n,
+ const struct qstr *nm)
+{
+ int err;
+
+ err = matches_name(c, &(*zn)->zbranch[*n], nm);
+ if (unlikely(err < 0))
+ return err;
+ if (err == NAME_MATCHES)
+ return 1;
+
+ if (err == NAME_GREATER) {
+ /* Look left */
+ while (1) {
+ err = tnc_prev(c, zn, n);
+ if (err == -ENOENT) {
+ ubifs_assert(*n == 0);
+ *n = -1;
+ return 0;
+ }
+ if (err < 0)
+ return err;
+ if (keys_cmp(c, &(*zn)->zbranch[*n].key, key)) {
+ /*
+ * We have found the branch after which we would
+ * like to insert, but inserting in this znode
+ * may still be wrong. Consider the following 3
+ * znodes, in the case where we are resolving a
+ * collision with Key2.
+ *
+ * znode zp
+ * ----------------------
+ * level 1 | Key0 | Key1 |
+ * -----------------------
+ * | |
+ * znode za | | znode zb
+ * ------------ ------------
+ * level 0 | Key0 | | Key2 |
+ * ------------ ------------
+ *
+ * The lookup finds Key2 in znode zb. Lets say
+ * there is no match and the name is greater so
+ * we look left. When we find Key0, we end up
+ * here. If we return now, we will insert into
+ * znode za at slot n = 1. But that is invalid
+ * according to the parent's keys. Key2 must
+ * be inserted into znode zb.
+ *
+ * Note, this problem is not relevant for the
+ * case when we go right, because
+ * 'tnc_insert()' would correct the parent key.
+ */
+ if (*n == (*zn)->child_cnt - 1) {
+ err = tnc_next(c, zn, n);
+ if (err) {
+ /* Should be impossible */
+ ubifs_assert(0);
+ if (err == -ENOENT)
+ err = -EINVAL;
+ return err;
+ }
+ ubifs_assert(*n == 0);
+ *n = -1;
+ }
+ return 0;
+ }
+ err = matches_name(c, &(*zn)->zbranch[*n], nm);
+ if (err < 0)
+ return err;
+ if (err == NAME_LESS)
+ return 0;
+ if (err == NAME_MATCHES)
+ return 1;
+ ubifs_assert(err == NAME_GREATER);
+ }
+ } else {
+ int nn = *n;
+ struct ubifs_znode *znode = *zn;
+
+ /* Look right */
+ while (1) {
+ err = tnc_next(c, &znode, &nn);
+ if (err == -ENOENT)
+ return 0;
+ if (err < 0)
+ return err;
+ if (keys_cmp(c, &znode->zbranch[nn].key, key))
+ return 0;
+ err = matches_name(c, &znode->zbranch[nn], nm);
+ if (err < 0)
+ return err;
+ if (err == NAME_GREATER)
+ return 0;
+ *zn = znode;
+ *n = nn;
+ if (err == NAME_MATCHES)
+ return 1;
+ ubifs_assert(err == NAME_LESS);
+ }
+ }
+}
+
+/**
+ * fallible_matches_name - determine if a dent matches a given name.
+ * @c: UBIFS file-system description object
+ * @zbr: zbranch of dent
+ * @nm: name to match
+ *
+ * This is a "fallible" version of 'matches_name()' function which does not
+ * panic if the direntry/xentry referred by @zbr does not exist on the media.
+ *
+ * This function checks if xentry/direntry referred by zbranch @zbr matches name
+ * @nm. Returns %NAME_MATCHES it does, %NAME_LESS if the name referred by @zbr
+ * is less than @nm, %NAME_GREATER if it is greater than @nm, and @NOT_ON_MEDIA
+ * if xentry/direntry referred by @zbr does not exist on the media. A negative
+ * error code is returned in case of failure.
+ */
+static int fallible_matches_name(struct ubifs_info *c,
+ struct ubifs_zbranch *zbr,
+ const struct qstr *nm)
+{
+ struct ubifs_dent_node *dent;
+ int nlen, err;
+
+ /* If possible, match against the dent in the leaf node cache */
+ if (!zbr->leaf) {
+ dent = kmalloc(zbr->len, GFP_NOFS);
+ if (!dent)
+ return -ENOMEM;
+
+ err = fallible_read_node(c, &zbr->key, zbr, dent);
+ if (err < 0)
+ goto out_free;
+ if (err == 0) {
+ /* The node was not present */
+ err = NOT_ON_MEDIA;
+ goto out_free;
+ }
+ ubifs_assert(err == 1);
+
+ err = lnc_add_directly(c, zbr, dent);
+ if (err)
+ goto out_free;
+ } else
+ dent = zbr->leaf;
+
+ nlen = le16_to_cpu(dent->nlen);
+ err = memcmp(dent->name, nm->name, min_t(int, nlen, nm->len));
+ if (err == 0) {
+ if (nlen == nm->len)
+ return NAME_MATCHES;
+ else if (nlen < nm->len)
+ return NAME_LESS;
+ else
+ return NAME_GREATER;
+ } else if (err < 0)
+ return NAME_LESS;
+ else
+ return NAME_GREATER;
+
+out_free:
+ kfree(dent);
+ return err;
+}
+
+/**
+ * fallible_resolve_collision - resolve a collision even if nodes are missing.
+ * @c: UBIFS file-system description object
+ * @key: key
+ * @zn: znode is returned here
+ * @n: branch number is passed and returned here
+ * @nm: name of directory entry
+ * @adding: indicates caller is adding a key to the TNC
+ *
+ * This is a "fallible" version of the 'resolve_collision()' function which
+ * does not panic if one of the nodes referred to by TNC does not exist on the
+ * media. This may happen when replaying the journal if a deleted node was
+ * Garbage-collected and the commit was not done. A branch that refers to a node
+ * that is not present is called a dangling branch. The following are the return
+ * codes for this function:
+ * o if @nm was found, %1 is returned and @zn and @n are set to the found
+ * branch;
+ * o if we are @adding and @nm was not found, %0 is returned;
+ * o if we are not @adding and @nm was not found, but a dangling branch was
+ * found, then %1 is returned and @zn and @n are set to the dangling branch;
+ * o a negative error code is returned in case of failure.
+ */
+static int fallible_resolve_collision(struct ubifs_info *c,
+ const union ubifs_key *key,
+ struct ubifs_znode **zn, int *n,
+ const struct qstr *nm, int adding)
+{
+ struct ubifs_znode *o_znode = NULL, *znode = *zn;
+ int uninitialized_var(o_n), err, cmp, unsure = 0, nn = *n;
+
+ cmp = fallible_matches_name(c, &znode->zbranch[nn], nm);
+ if (unlikely(cmp < 0))
+ return cmp;
+ if (cmp == NAME_MATCHES)
+ return 1;
+ if (cmp == NOT_ON_MEDIA) {
+ o_znode = znode;
+ o_n = nn;
+ /*
+ * We are unlucky and hit a dangling branch straight away.
+ * Now we do not really know where to go to find the needed
+ * branch - to the left or to the right. Well, let's try left.
+ */
+ unsure = 1;
+ } else if (!adding)
+ unsure = 1; /* Remove a dangling branch wherever it is */
+
+ if (cmp == NAME_GREATER || unsure) {
+ /* Look left */
+ while (1) {
+ err = tnc_prev(c, zn, n);
+ if (err == -ENOENT) {
+ ubifs_assert(*n == 0);
+ *n = -1;
+ break;
+ }
+ if (err < 0)
+ return err;
+ if (keys_cmp(c, &(*zn)->zbranch[*n].key, key)) {
+ /* See comments in 'resolve_collision()' */
+ if (*n == (*zn)->child_cnt - 1) {
+ err = tnc_next(c, zn, n);
+ if (err) {
+ /* Should be impossible */
+ ubifs_assert(0);
+ if (err == -ENOENT)
+ err = -EINVAL;
+ return err;
+ }
+ ubifs_assert(*n == 0);
+ *n = -1;
+ }
+ break;
+ }
+ err = fallible_matches_name(c, &(*zn)->zbranch[*n], nm);
+ if (err < 0)
+ return err;
+ if (err == NAME_MATCHES)
+ return 1;
+ if (err == NOT_ON_MEDIA) {
+ o_znode = *zn;
+ o_n = *n;
+ continue;
+ }
+ if (!adding)
+ continue;
+ if (err == NAME_LESS)
+ break;
+ else
+ unsure = 0;
+ }
+ }
+
+ if (cmp == NAME_LESS || unsure) {
+ /* Look right */
+ *zn = znode;
+ *n = nn;
+ while (1) {
+ err = tnc_next(c, &znode, &nn);
+ if (err == -ENOENT)
+ break;
+ if (err < 0)
+ return err;
+ if (keys_cmp(c, &znode->zbranch[nn].key, key))
+ break;
+ err = fallible_matches_name(c, &znode->zbranch[nn], nm);
+ if (err < 0)
+ return err;
+ if (err == NAME_GREATER)
+ break;
+ *zn = znode;
+ *n = nn;
+ if (err == NAME_MATCHES)
+ return 1;
+ if (err == NOT_ON_MEDIA) {
+ o_znode = znode;
+ o_n = nn;
+ }
+ }
+ }
+
+ /* Never match a dangling branch when adding */
+ if (adding || !o_znode)
+ return 0;
+
+ dbg_mntk(key, "dangling match LEB %d:%d len %d key ",
+ o_znode->zbranch[o_n].lnum, o_znode->zbranch[o_n].offs,
+ o_znode->zbranch[o_n].len);
+ *zn = o_znode;
+ *n = o_n;
+ return 1;
+}
+
+/**
+ * matches_position - determine if a zbranch matches a given position.
+ * @zbr: zbranch of dent
+ * @lnum: LEB number of dent to match
+ * @offs: offset of dent to match
+ *
+ * This function returns %1 if @lnum:@offs matches, and %0 otherwise.
+ */
+static int matches_position(struct ubifs_zbranch *zbr, int lnum, int offs)
+{
+ if (zbr->lnum == lnum && zbr->offs == offs)
+ return 1;
+ else
+ return 0;
+}
+
+/**
+ * resolve_collision_directly - resolve a collision directly.
+ * @c: UBIFS file-system description object
+ * @key: key of directory entry
+ * @zn: znode is passed and returned here
+ * @n: zbranch number is passed and returned here
+ * @lnum: LEB number of dent node to match
+ * @offs: offset of dent node to match
+ *
+ * This function is used for "hashed" keys to make sure the found directory or
+ * extended attribute entry node is what was looked for. It is used when the
+ * flash address of the right node is known (@lnum:@offs) which makes it much
+ * easier to resolve collisions (no need to read entries and match full
+ * names). This function returns %1 and sets @zn and @n if the collision is
+ * resolved, %0 if @lnum:@offs is not found and @zn and @n are set to the
+ * previous directory entry. Otherwise a negative error code is returned.
+ */
+static int resolve_collision_directly(struct ubifs_info *c,
+ const union ubifs_key *key,
+ struct ubifs_znode **zn, int *n,
+ int lnum, int offs)
+{
+ struct ubifs_znode *znode;
+ int nn, err;
+
+ znode = *zn;
+ nn = *n;
+ if (matches_position(&znode->zbranch[nn], lnum, offs))
+ return 1;
+
+ /* Look left */
+ while (1) {
+ err = tnc_prev(c, &znode, &nn);
+ if (err == -ENOENT)
+ break;
+ if (err < 0)
+ return err;
+ if (keys_cmp(c, &znode->zbranch[nn].key, key))
+ break;
+ if (matches_position(&znode->zbranch[nn], lnum, offs)) {
+ *zn = znode;
+ *n = nn;
+ return 1;
+ }
+ }
+
+ /* Look right */
+ znode = *zn;
+ nn = *n;
+ while (1) {
+ err = tnc_next(c, &znode, &nn);
+ if (err == -ENOENT)
+ return 0;
+ if (err < 0)
+ return err;
+ if (keys_cmp(c, &znode->zbranch[nn].key, key))
+ return 0;
+ *zn = znode;
+ *n = nn;
+ if (matches_position(&znode->zbranch[nn], lnum, offs))
+ return 1;
+ }
+}
+
+/**
+ * dirty_cow_bottom_up - dirty a znode and its ancestors.
+ * @c: UBIFS file-system description object
+ * @znode: znode to dirty
+ *
+ * If we do not have a unique key that resides in a znode, then we cannot
+ * dirty that znode from the top down (i.e. by using lookup_level0_dirty)
+ * This function records the path back to the last dirty ancestor, and then
+ * dirties the znodes on that path.
+ */
+static struct ubifs_znode *dirty_cow_bottom_up(struct ubifs_info *c,
+ struct ubifs_znode *znode)
+{
+ struct ubifs_znode *zp;
+ int *path = c->bottom_up_buf, p = 0;
+
+ ubifs_assert(c->zroot.znode);
+ ubifs_assert(znode);
+ if (c->zroot.znode->level > BOTTOM_UP_HEIGHT) {
+ kfree(c->bottom_up_buf);
+ c->bottom_up_buf = kmalloc(c->zroot.znode->level * sizeof(int),
+ GFP_NOFS);
+ if (!c->bottom_up_buf)
+ return ERR_PTR(-ENOMEM);
+ path = c->bottom_up_buf;
+ }
+ if (c->zroot.znode->level) {
+ /* Go up until parent is dirty */
+ while (1) {
+ int n;
+
+ zp = znode->parent;
+ if (!zp)
+ break;
+ n = znode->iip;
+ ubifs_assert(p < c->zroot.znode->level);
+ path[p++] = n;
+ if (!zp->cnext && ubifs_zn_dirty(znode))
+ break;
+ znode = zp;
+ }
+ }
+
+ /* Come back down, dirtying as we go */
+ while (1) {
+ struct ubifs_zbranch *zbr;
+
+ zp = znode->parent;
+ if (zp) {
+ ubifs_assert(path[p - 1] >= 0);
+ ubifs_assert(path[p - 1] < zp->child_cnt);
+ zbr = &zp->zbranch[path[--p]];
+ znode = dirty_cow_znode(c, zbr);
+ } else {
+ ubifs_assert(znode == c->zroot.znode);
+ znode = dirty_cow_znode(c, &c->zroot);
+ }
+ if (IS_ERR(znode) || !p)
+ break;
+ ubifs_assert(path[p - 1] >= 0);
+ ubifs_assert(path[p - 1] < znode->child_cnt);
+ znode = znode->zbranch[path[p - 1]].znode;
+ }
+
+ return znode;
+}
+
+/**
+ * ubifs_lookup_level0 - search for zero-level znode.
+ * @c: UBIFS file-system description object
+ * @key: key to lookup
+ * @zn: znode is returned here
+ * @n: znode branch slot number is returned here
+ *
+ * This function looks up the TNC tree and search for zero-level znode which
+ * refers key @key. The found zero-level znode is returned in @zn. There are 3
+ * cases:
+ * o exact match, i.e. the found zero-level znode contains key @key, then %1
+ * is returned and slot number of the matched branch is stored in @n;
+ * o not exact match, which means that zero-level znode does not contain
+ * @key, then %0 is returned and slot number of the closest branch is stored
+ * in @n;
+ * o @key is so small that it is even less than the lowest key of the
+ * leftmost zero-level node, then %0 is returned and %0 is stored in @n.
+ *
+ * Note, when the TNC tree is traversed, some znodes may be absent, then this
+ * function reads corresponding indexing nodes and inserts them to TNC. In
+ * case of failure, a negative error code is returned.
+ */
+int ubifs_lookup_level0(struct ubifs_info *c, const union ubifs_key *key,
+ struct ubifs_znode **zn, int *n)
+{
+ int err, exact;
+ struct ubifs_znode *znode;
+ unsigned long time = get_seconds();
+
+ dbg_tnck(key, "search key ");
+ ubifs_assert(key_type(c, key) < UBIFS_INVALID_KEY);
+
+ znode = c->zroot.znode;
+ if (unlikely(!znode)) {
+ znode = ubifs_load_znode(c, &c->zroot, NULL, 0);
+ if (IS_ERR(znode))
+ return PTR_ERR(znode);
+ }
+
+ znode->time = time;
+
+ while (1) {
+ struct ubifs_zbranch *zbr;
+
+ exact = ubifs_search_zbranch(c, znode, key, n);
+
+ if (znode->level == 0)
+ break;
+
+ if (*n < 0)
+ *n = 0;
+ zbr = &znode->zbranch[*n];
+
+ if (zbr->znode) {
+ znode->time = time;
+ znode = zbr->znode;
+ continue;
+ }
+
+ /* znode is not in TNC cache, load it from the media */
+ znode = ubifs_load_znode(c, zbr, znode, *n);
+ if (IS_ERR(znode))
+ return PTR_ERR(znode);
+ }
+
+ *zn = znode;
+ if (exact || !is_hash_key(c, key) || *n != -1) {
+ dbg_tnc("found %d, lvl %d, n %d", exact, znode->level, *n);
+ return exact;
+ }
+
+ /*
+ * Here is a tricky place. We have not found the key and this is a
+ * "hashed" key, which may collide. The rest of the code deals with
+ * situations like this:
+ *
+ * | 3 | 5 |
+ * / \
+ * | 3 | 5 | | 6 | 7 | (x)
+ *
+ * Or more a complex example:
+ *
+ * | 1 | 5 |
+ * / \
+ * | 1 | 3 | | 5 | 8 |
+ * \ /
+ * | 5 | 5 | | 6 | 7 | (x)
+ *
+ * In the examples, if we are looking for key "5", we may reach nodes
+ * marked with "(x)". In this case what we have do is to look at the
+ * left and see if there is "5" key there. If there is, we have to
+ * return it.
+ *
+ * Note, this whole situation is possible because we allow to have
+ * elements which are equivalent to the next key in the parent in the
+ * children of current znode. For example, this happens if we split a
+ * znode like this: | 3 | 5 | 5 | 6 | 7 |, which results in something
+ * like this:
+ * | 3 | 5 |
+ * / \
+ * | 3 | 5 | | 5 | 6 | 7 |
+ * ^
+ * And this becomes what is at the first "picture" after key "5" marked
+ * with "^" is removed. What could be done is we could prohibit
+ * splitting in the middle of the colliding sequence. Also, when
+ * removing the leftmost key, we would have to correct the key of the
+ * parent node, which would introduce additional complications. Namely,
+ * if we changed the leftmost key of the parent znode, the garbage
+ * collector would be unable to find it (GC is doing this when GC'ing
+ * indexing LEBs). Although we already have an additional RB-tree where
+ * we save such changed znodes (see 'ins_clr_old_idx_znode()') until
+ * after the commit. But anyway, this does not look easy to implement
+ * so we did not try this.
+ */
+ err = tnc_prev(c, &znode, n);
+ if (err == -ENOENT) {
+ dbg_tnc("found 0, lvl %d, n -1", znode->level);
+ *n = -1;
+ return 0;
+ }
+ if (unlikely(err < 0))
+ return err;
+ if (keys_cmp(c, key, &znode->zbranch[*n].key)) {
+ dbg_tnc("found 0, lvl %d, n -1", znode->level);
+ *n = -1;
+ return 0;
+ }
+
+ dbg_tnc("found 1, lvl %d, n %d", znode->level, *n);
+ *zn = znode;
+ return 1;
+}
+
+/**
+ * lookup_level0_dirty - search for zero-level znode dirtying.
+ * @c: UBIFS file-system description object
+ * @key: key to lookup
+ * @zn: znode is returned here
+ * @n: znode branch slot number is returned here
+ *
+ * This function looks up the TNC tree and search for zero-level znode which
+ * refers key @key. The found zero-level znode is returned in @zn. There are 3
+ * cases:
+ * o exact match, i.e. the found zero-level znode contains key @key, then %1
+ * is returned and slot number of the matched branch is stored in @n;
+ * o not exact match, which means that zero-level znode does not contain @key
+ * then %0 is returned and slot number of the closed branch is stored in
+ * @n;
+ * o @key is so small that it is even less than the lowest key of the
+ * leftmost zero-level node, then %0 is returned and %-1 is stored in @n.
+ *
+ * Additionally all znodes in the path from the root to the located zero-level
+ * znode are marked as dirty.
+ *
+ * Note, when the TNC tree is traversed, some znodes may be absent, then this
+ * function reads corresponding indexing nodes and inserts them to TNC. In
+ * case of failure, a negative error code is returned.
+ */
+static int lookup_level0_dirty(struct ubifs_info *c, const union ubifs_key *key,
+ struct ubifs_znode **zn, int *n)
+{
+ int err, exact;
+ struct ubifs_znode *znode;
+ unsigned long time = get_seconds();
+
+ dbg_tnck(key, "search and dirty key ");
+
+ znode = c->zroot.znode;
+ if (unlikely(!znode)) {
+ znode = ubifs_load_znode(c, &c->zroot, NULL, 0);
+ if (IS_ERR(znode))
+ return PTR_ERR(znode);
+ }
+
+ znode = dirty_cow_znode(c, &c->zroot);
+ if (IS_ERR(znode))
+ return PTR_ERR(znode);
+
+ znode->time = time;
+
+ while (1) {
+ struct ubifs_zbranch *zbr;
+
+ exact = ubifs_search_zbranch(c, znode, key, n);
+
+ if (znode->level == 0)
+ break;
+
+ if (*n < 0)
+ *n = 0;
+ zbr = &znode->zbranch[*n];
+
+ if (zbr->znode) {
+ znode->time = time;
+ znode = dirty_cow_znode(c, zbr);
+ if (IS_ERR(znode))
+ return PTR_ERR(znode);
+ continue;
+ }
+
+ /* znode is not in TNC cache, load it from the media */
+ znode = ubifs_load_znode(c, zbr, znode, *n);
+ if (IS_ERR(znode))
+ return PTR_ERR(znode);
+ znode = dirty_cow_znode(c, zbr);
+ if (IS_ERR(znode))
+ return PTR_ERR(znode);
+ }
+
+ *zn = znode;
+ if (exact || !is_hash_key(c, key) || *n != -1) {
+ dbg_tnc("found %d, lvl %d, n %d", exact, znode->level, *n);
+ return exact;
+ }
+
+ /*
+ * See huge comment at 'lookup_level0_dirty()' what is the rest of the
+ * code.
+ */
+ err = tnc_prev(c, &znode, n);
+ if (err == -ENOENT) {
+ *n = -1;
+ dbg_tnc("found 0, lvl %d, n -1", znode->level);
+ return 0;
+ }
+ if (unlikely(err < 0))
+ return err;
+ if (keys_cmp(c, key, &znode->zbranch[*n].key)) {
+ *n = -1;
+ dbg_tnc("found 0, lvl %d, n -1", znode->level);
+ return 0;
+ }
+
+ if (znode->cnext || !ubifs_zn_dirty(znode)) {
+ znode = dirty_cow_bottom_up(c, znode);
+ if (IS_ERR(znode))
+ return PTR_ERR(znode);
+ }
+
+ dbg_tnc("found 1, lvl %d, n %d", znode->level, *n);
+ *zn = znode;
+ return 1;
+}
+
+/**
+ * maybe_leb_gced - determine if a LEB may have been garbage collected.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number
+ * @gc_seq1: garbage collection sequence number
+ *
+ * This function determines if @lnum may have been garbage collected since
+ * sequence number @gc_seq1. If it may have been then %1 is returned, otherwise
+ * %0 is returned.
+ */
+static int maybe_leb_gced(struct ubifs_info *c, int lnum, int gc_seq1)
+{
+#ifndef __UBOOT__
+ int gc_seq2, gced_lnum;
+
+ gced_lnum = c->gced_lnum;
+ smp_rmb();
+ gc_seq2 = c->gc_seq;
+ /* Same seq means no GC */
+ if (gc_seq1 == gc_seq2)
+ return 0;
+ /* Different by more than 1 means we don't know */
+ if (gc_seq1 + 1 != gc_seq2)
+ return 1;
+ /*
+ * We have seen the sequence number has increased by 1. Now we need to
+ * be sure we read the right LEB number, so read it again.
+ */
+ smp_rmb();
+ if (gced_lnum != c->gced_lnum)
+ return 1;
+ /* Finally we can check lnum */
+ if (gced_lnum == lnum)
+ return 1;
+#else
+ /* No garbage collection in the read-only U-Boot implementation */
+#endif
+ return 0;
+}
+
+/**
+ * ubifs_tnc_locate - look up a file-system node and return it and its location.
+ * @c: UBIFS file-system description object
+ * @key: node key to lookup
+ * @node: the node is returned here
+ * @lnum: LEB number is returned here
+ * @offs: offset is returned here
+ *
+ * This function looks up and reads node with key @key. The caller has to make
+ * sure the @node buffer is large enough to fit the node. Returns zero in case
+ * of success, %-ENOENT if the node was not found, and a negative error code in
+ * case of failure. The node location can be returned in @lnum and @offs.
+ */
+int ubifs_tnc_locate(struct ubifs_info *c, const union ubifs_key *key,
+ void *node, int *lnum, int *offs)
+{
+ int found, n, err, safely = 0, gc_seq1;
+ struct ubifs_znode *znode;
+ struct ubifs_zbranch zbr, *zt;
+
+again:
+ mutex_lock(&c->tnc_mutex);
+ found = ubifs_lookup_level0(c, key, &znode, &n);
+ if (!found) {
+ err = -ENOENT;
+ goto out;
+ } else if (found < 0) {
+ err = found;
+ goto out;
+ }
+ zt = &znode->zbranch[n];
+ if (lnum) {
+ *lnum = zt->lnum;
+ *offs = zt->offs;
+ }
+ if (is_hash_key(c, key)) {
+ /*
+ * In this case the leaf node cache gets used, so we pass the
+ * address of the zbranch and keep the mutex locked
+ */
+ err = tnc_read_node_nm(c, zt, node);
+ goto out;
+ }
+ if (safely) {
+ err = ubifs_tnc_read_node(c, zt, node);
+ goto out;
+ }
+ /* Drop the TNC mutex prematurely and race with garbage collection */
+ zbr = znode->zbranch[n];
+ gc_seq1 = c->gc_seq;
+ mutex_unlock(&c->tnc_mutex);
+
+ if (ubifs_get_wbuf(c, zbr.lnum)) {
+ /* We do not GC journal heads */
+ err = ubifs_tnc_read_node(c, &zbr, node);
+ return err;
+ }
+
+ err = fallible_read_node(c, key, &zbr, node);
+ if (err <= 0 || maybe_leb_gced(c, zbr.lnum, gc_seq1)) {
+ /*
+ * The node may have been GC'ed out from under us so try again
+ * while keeping the TNC mutex locked.
+ */
+ safely = 1;
+ goto again;
+ }
+ return 0;
+
+out:
+ mutex_unlock(&c->tnc_mutex);
+ return err;
+}
+
+/**
+ * ubifs_tnc_get_bu_keys - lookup keys for bulk-read.
+ * @c: UBIFS file-system description object
+ * @bu: bulk-read parameters and results
+ *
+ * Lookup consecutive data node keys for the same inode that reside
+ * consecutively in the same LEB. This function returns zero in case of success
+ * and a negative error code in case of failure.
+ *
+ * Note, if the bulk-read buffer length (@bu->buf_len) is known, this function
+ * makes sure bulk-read nodes fit the buffer. Otherwise, this function prepares
+ * maximum possible amount of nodes for bulk-read.
+ */
+int ubifs_tnc_get_bu_keys(struct ubifs_info *c, struct bu_info *bu)
+{
+ int n, err = 0, lnum = -1, uninitialized_var(offs);
+ int uninitialized_var(len);
+ unsigned int block = key_block(c, &bu->key);
+ struct ubifs_znode *znode;
+
+ bu->cnt = 0;
+ bu->blk_cnt = 0;
+ bu->eof = 0;
+
+ mutex_lock(&c->tnc_mutex);
+ /* Find first key */
+ err = ubifs_lookup_level0(c, &bu->key, &znode, &n);
+ if (err < 0)
+ goto out;
+ if (err) {
+ /* Key found */
+ len = znode->zbranch[n].len;
+ /* The buffer must be big enough for at least 1 node */
+ if (len > bu->buf_len) {
+ err = -EINVAL;
+ goto out;
+ }
+ /* Add this key */
+ bu->zbranch[bu->cnt++] = znode->zbranch[n];
+ bu->blk_cnt += 1;
+ lnum = znode->zbranch[n].lnum;
+ offs = ALIGN(znode->zbranch[n].offs + len, 8);
+ }
+ while (1) {
+ struct ubifs_zbranch *zbr;
+ union ubifs_key *key;
+ unsigned int next_block;
+
+ /* Find next key */
+ err = tnc_next(c, &znode, &n);
+ if (err)
+ goto out;
+ zbr = &znode->zbranch[n];
+ key = &zbr->key;
+ /* See if there is another data key for this file */
+ if (key_inum(c, key) != key_inum(c, &bu->key) ||
+ key_type(c, key) != UBIFS_DATA_KEY) {
+ err = -ENOENT;
+ goto out;
+ }
+ if (lnum < 0) {
+ /* First key found */
+ lnum = zbr->lnum;
+ offs = ALIGN(zbr->offs + zbr->len, 8);
+ len = zbr->len;
+ if (len > bu->buf_len) {
+ err = -EINVAL;
+ goto out;
+ }
+ } else {
+ /*
+ * The data nodes must be in consecutive positions in
+ * the same LEB.
+ */
+ if (zbr->lnum != lnum || zbr->offs != offs)
+ goto out;
+ offs += ALIGN(zbr->len, 8);
+ len = ALIGN(len, 8) + zbr->len;
+ /* Must not exceed buffer length */
+ if (len > bu->buf_len)
+ goto out;
+ }
+ /* Allow for holes */
+ next_block = key_block(c, key);
+ bu->blk_cnt += (next_block - block - 1);
+ if (bu->blk_cnt >= UBIFS_MAX_BULK_READ)
+ goto out;
+ block = next_block;
+ /* Add this key */
+ bu->zbranch[bu->cnt++] = *zbr;
+ bu->blk_cnt += 1;
+ /* See if we have room for more */
+ if (bu->cnt >= UBIFS_MAX_BULK_READ)
+ goto out;
+ if (bu->blk_cnt >= UBIFS_MAX_BULK_READ)
+ goto out;
+ }
+out:
+ if (err == -ENOENT) {
+ bu->eof = 1;
+ err = 0;
+ }
+ bu->gc_seq = c->gc_seq;
+ mutex_unlock(&c->tnc_mutex);
+ if (err)
+ return err;
+ /*
+ * An enormous hole could cause bulk-read to encompass too many
+ * page cache pages, so limit the number here.
+ */
+ if (bu->blk_cnt > UBIFS_MAX_BULK_READ)
+ bu->blk_cnt = UBIFS_MAX_BULK_READ;
+ /*
+ * Ensure that bulk-read covers a whole number of page cache
+ * pages.
+ */
+ if (UBIFS_BLOCKS_PER_PAGE == 1 ||
+ !(bu->blk_cnt & (UBIFS_BLOCKS_PER_PAGE - 1)))
+ return 0;
+ if (bu->eof) {
+ /* At the end of file we can round up */
+ bu->blk_cnt += UBIFS_BLOCKS_PER_PAGE - 1;
+ return 0;
+ }
+ /* Exclude data nodes that do not make up a whole page cache page */
+ block = key_block(c, &bu->key) + bu->blk_cnt;
+ block &= ~(UBIFS_BLOCKS_PER_PAGE - 1);
+ while (bu->cnt) {
+ if (key_block(c, &bu->zbranch[bu->cnt - 1].key) < block)
+ break;
+ bu->cnt -= 1;
+ }
+ return 0;
+}
+
+/**
+ * read_wbuf - bulk-read from a LEB with a wbuf.
+ * @wbuf: wbuf that may overlap the read
+ * @buf: buffer into which to read
+ * @len: read length
+ * @lnum: LEB number from which to read
+ * @offs: offset from which to read
+ *
+ * This functions returns %0 on success or a negative error code on failure.
+ */
+static int read_wbuf(struct ubifs_wbuf *wbuf, void *buf, int len, int lnum,
+ int offs)
+{
+ const struct ubifs_info *c = wbuf->c;
+ int rlen, overlap;
+
+ dbg_io("LEB %d:%d, length %d", lnum, offs, len);
+ ubifs_assert(wbuf && lnum >= 0 && lnum < c->leb_cnt && offs >= 0);
+ ubifs_assert(!(offs & 7) && offs < c->leb_size);
+ ubifs_assert(offs + len <= c->leb_size);
+
+ spin_lock(&wbuf->lock);
+ overlap = (lnum == wbuf->lnum && offs + len > wbuf->offs);
+ if (!overlap) {
+ /* We may safely unlock the write-buffer and read the data */
+ spin_unlock(&wbuf->lock);
+ return ubifs_leb_read(c, lnum, buf, offs, len, 0);
+ }
+
+ /* Don't read under wbuf */
+ rlen = wbuf->offs - offs;
+ if (rlen < 0)
+ rlen = 0;
+
+ /* Copy the rest from the write-buffer */
+ memcpy(buf + rlen, wbuf->buf + offs + rlen - wbuf->offs, len - rlen);
+ spin_unlock(&wbuf->lock);
+
+ if (rlen > 0)
+ /* Read everything that goes before write-buffer */
+ return ubifs_leb_read(c, lnum, buf, offs, rlen, 0);
+
+ return 0;
+}
+
+/**
+ * validate_data_node - validate data nodes for bulk-read.
+ * @c: UBIFS file-system description object
+ * @buf: buffer containing data node to validate
+ * @zbr: zbranch of data node to validate
+ *
+ * This functions returns %0 on success or a negative error code on failure.
+ */
+static int validate_data_node(struct ubifs_info *c, void *buf,
+ struct ubifs_zbranch *zbr)
+{
+ union ubifs_key key1;
+ struct ubifs_ch *ch = buf;
+ int err, len;
+
+ if (ch->node_type != UBIFS_DATA_NODE) {
+ ubifs_err(c, "bad node type (%d but expected %d)",
+ ch->node_type, UBIFS_DATA_NODE);
+ goto out_err;
+ }
+
+ err = ubifs_check_node(c, buf, zbr->lnum, zbr->offs, 0, 0);
+ if (err) {
+ ubifs_err(c, "expected node type %d", UBIFS_DATA_NODE);
+ goto out;
+ }
+
+ len = le32_to_cpu(ch->len);
+ if (len != zbr->len) {
+ ubifs_err(c, "bad node length %d, expected %d", len, zbr->len);
+ goto out_err;
+ }
+
+ /* Make sure the key of the read node is correct */
+ key_read(c, buf + UBIFS_KEY_OFFSET, &key1);
+ if (!keys_eq(c, &zbr->key, &key1)) {
+ ubifs_err(c, "bad key in node at LEB %d:%d",
+ zbr->lnum, zbr->offs);
+ dbg_tnck(&zbr->key, "looked for key ");
+ dbg_tnck(&key1, "found node's key ");
+ goto out_err;
+ }
+
+ return 0;
+
+out_err:
+ err = -EINVAL;
+out:
+ ubifs_err(c, "bad node at LEB %d:%d", zbr->lnum, zbr->offs);
+ ubifs_dump_node(c, buf);
+ dump_stack();
+ return err;
+}
+
+/**
+ * ubifs_tnc_bulk_read - read a number of data nodes in one go.
+ * @c: UBIFS file-system description object
+ * @bu: bulk-read parameters and results
+ *
+ * This functions reads and validates the data nodes that were identified by the
+ * 'ubifs_tnc_get_bu_keys()' function. This functions returns %0 on success,
+ * -EAGAIN to indicate a race with GC, or another negative error code on
+ * failure.
+ */
+int ubifs_tnc_bulk_read(struct ubifs_info *c, struct bu_info *bu)
+{
+ int lnum = bu->zbranch[0].lnum, offs = bu->zbranch[0].offs, len, err, i;
+ struct ubifs_wbuf *wbuf;
+ void *buf;
+
+ len = bu->zbranch[bu->cnt - 1].offs;
+ len += bu->zbranch[bu->cnt - 1].len - offs;
+ if (len > bu->buf_len) {
+ ubifs_err(c, "buffer too small %d vs %d", bu->buf_len, len);
+ return -EINVAL;
+ }
+
+ /* Do the read */
+ wbuf = ubifs_get_wbuf(c, lnum);
+ if (wbuf)
+ err = read_wbuf(wbuf, bu->buf, len, lnum, offs);
+ else
+ err = ubifs_leb_read(c, lnum, bu->buf, offs, len, 0);
+
+ /* Check for a race with GC */
+ if (maybe_leb_gced(c, lnum, bu->gc_seq))
+ return -EAGAIN;
+
+ if (err && err != -EBADMSG) {
+ ubifs_err(c, "failed to read from LEB %d:%d, error %d",
+ lnum, offs, err);
+ dump_stack();
+ dbg_tnck(&bu->key, "key ");
+ return err;
+ }
+
+ /* Validate the nodes read */
+ buf = bu->buf;
+ for (i = 0; i < bu->cnt; i++) {
+ err = validate_data_node(c, buf, &bu->zbranch[i]);
+ if (err)
+ return err;
+ buf = buf + ALIGN(bu->zbranch[i].len, 8);
+ }
+
+ return 0;
+}
+
+/**
+ * do_lookup_nm- look up a "hashed" node.
+ * @c: UBIFS file-system description object
+ * @key: node key to lookup
+ * @node: the node is returned here
+ * @nm: node name
+ *
+ * This function look up and reads a node which contains name hash in the key.
+ * Since the hash may have collisions, there may be many nodes with the same
+ * key, so we have to sequentially look to all of them until the needed one is
+ * found. This function returns zero in case of success, %-ENOENT if the node
+ * was not found, and a negative error code in case of failure.
+ */
+static int do_lookup_nm(struct ubifs_info *c, const union ubifs_key *key,
+ void *node, const struct qstr *nm)
+{
+ int found, n, err;
+ struct ubifs_znode *znode;
+
+ dbg_tnck(key, "name '%.*s' key ", nm->len, nm->name);
+ mutex_lock(&c->tnc_mutex);
+ found = ubifs_lookup_level0(c, key, &znode, &n);
+ if (!found) {
+ err = -ENOENT;
+ goto out_unlock;
+ } else if (found < 0) {
+ err = found;
+ goto out_unlock;
+ }
+
+ ubifs_assert(n >= 0);
+
+ err = resolve_collision(c, key, &znode, &n, nm);
+ dbg_tnc("rc returned %d, znode %p, n %d", err, znode, n);
+ if (unlikely(err < 0))
+ goto out_unlock;
+ if (err == 0) {
+ err = -ENOENT;
+ goto out_unlock;
+ }
+
+ err = tnc_read_node_nm(c, &znode->zbranch[n], node);
+
+out_unlock:
+ mutex_unlock(&c->tnc_mutex);
+ return err;
+}
+
+/**
+ * ubifs_tnc_lookup_nm - look up a "hashed" node.
+ * @c: UBIFS file-system description object
+ * @key: node key to lookup
+ * @node: the node is returned here
+ * @nm: node name
+ *
+ * This function look up and reads a node which contains name hash in the key.
+ * Since the hash may have collisions, there may be many nodes with the same
+ * key, so we have to sequentially look to all of them until the needed one is
+ * found. This function returns zero in case of success, %-ENOENT if the node
+ * was not found, and a negative error code in case of failure.
+ */
+int ubifs_tnc_lookup_nm(struct ubifs_info *c, const union ubifs_key *key,
+ void *node, const struct qstr *nm)
+{
+ int err, len;
+ const struct ubifs_dent_node *dent = node;
+
+ /*
+ * We assume that in most of the cases there are no name collisions and
+ * 'ubifs_tnc_lookup()' returns us the right direntry.
+ */
+ err = ubifs_tnc_lookup(c, key, node);
+ if (err)
+ return err;
+
+ len = le16_to_cpu(dent->nlen);
+ if (nm->len == len && !memcmp(dent->name, nm->name, len))
+ return 0;
+
+ /*
+ * Unluckily, there are hash collisions and we have to iterate over
+ * them look at each direntry with colliding name hash sequentially.
+ */
+ return do_lookup_nm(c, key, node, nm);
+}
+
+/**
+ * correct_parent_keys - correct parent znodes' keys.
+ * @c: UBIFS file-system description object
+ * @znode: znode to correct parent znodes for
+ *
+ * This is a helper function for 'tnc_insert()'. When the key of the leftmost
+ * zbranch changes, keys of parent znodes have to be corrected. This helper
+ * function is called in such situations and corrects the keys if needed.
+ */
+static void correct_parent_keys(const struct ubifs_info *c,
+ struct ubifs_znode *znode)
+{
+ union ubifs_key *key, *key1;
+
+ ubifs_assert(znode->parent);
+ ubifs_assert(znode->iip == 0);
+
+ key = &znode->zbranch[0].key;
+ key1 = &znode->parent->zbranch[0].key;
+
+ while (keys_cmp(c, key, key1) < 0) {
+ key_copy(c, key, key1);
+ znode = znode->parent;
+ znode->alt = 1;
+ if (!znode->parent || znode->iip)
+ break;
+ key1 = &znode->parent->zbranch[0].key;
+ }
+}
+
+/**
+ * insert_zbranch - insert a zbranch into a znode.
+ * @znode: znode into which to insert
+ * @zbr: zbranch to insert
+ * @n: slot number to insert to
+ *
+ * This is a helper function for 'tnc_insert()'. UBIFS does not allow "gaps" in
+ * znode's array of zbranches and keeps zbranches consolidated, so when a new
+ * zbranch has to be inserted to the @znode->zbranches[]' array at the @n-th
+ * slot, zbranches starting from @n have to be moved right.
+ */
+static void insert_zbranch(struct ubifs_znode *znode,
+ const struct ubifs_zbranch *zbr, int n)
+{
+ int i;
+
+ ubifs_assert(ubifs_zn_dirty(znode));
+
+ if (znode->level) {
+ for (i = znode->child_cnt; i > n; i--) {
+ znode->zbranch[i] = znode->zbranch[i - 1];
+ if (znode->zbranch[i].znode)
+ znode->zbranch[i].znode->iip = i;
+ }
+ if (zbr->znode)
+ zbr->znode->iip = n;
+ } else
+ for (i = znode->child_cnt; i > n; i--)
+ znode->zbranch[i] = znode->zbranch[i - 1];
+
+ znode->zbranch[n] = *zbr;
+ znode->child_cnt += 1;
+
+ /*
+ * After inserting at slot zero, the lower bound of the key range of
+ * this znode may have changed. If this znode is subsequently split
+ * then the upper bound of the key range may change, and furthermore
+ * it could change to be lower than the original lower bound. If that
+ * happens, then it will no longer be possible to find this znode in the
+ * TNC using the key from the index node on flash. That is bad because
+ * if it is not found, we will assume it is obsolete and may overwrite
+ * it. Then if there is an unclean unmount, we will start using the
+ * old index which will be broken.
+ *
+ * So we first mark znodes that have insertions at slot zero, and then
+ * if they are split we add their lnum/offs to the old_idx tree.
+ */
+ if (n == 0)
+ znode->alt = 1;
+}
+
+/**
+ * tnc_insert - insert a node into TNC.
+ * @c: UBIFS file-system description object
+ * @znode: znode to insert into
+ * @zbr: branch to insert
+ * @n: slot number to insert new zbranch to
+ *
+ * This function inserts a new node described by @zbr into znode @znode. If
+ * znode does not have a free slot for new zbranch, it is split. Parent znodes
+ * are splat as well if needed. Returns zero in case of success or a negative
+ * error code in case of failure.
+ */
+static int tnc_insert(struct ubifs_info *c, struct ubifs_znode *znode,
+ struct ubifs_zbranch *zbr, int n)
+{
+ struct ubifs_znode *zn, *zi, *zp;
+ int i, keep, move, appending = 0;
+ union ubifs_key *key = &zbr->key, *key1;
+
+ ubifs_assert(n >= 0 && n <= c->fanout);
+
+ /* Implement naive insert for now */
+again:
+ zp = znode->parent;
+ if (znode->child_cnt < c->fanout) {
+ ubifs_assert(n != c->fanout);
+ dbg_tnck(key, "inserted at %d level %d, key ", n, znode->level);
+
+ insert_zbranch(znode, zbr, n);
+
+ /* Ensure parent's key is correct */
+ if (n == 0 && zp && znode->iip == 0)
+ correct_parent_keys(c, znode);
+
+ return 0;
+ }
+
+ /*
+ * Unfortunately, @znode does not have more empty slots and we have to
+ * split it.
+ */
+ dbg_tnck(key, "splitting level %d, key ", znode->level);
+
+ if (znode->alt)
+ /*
+ * We can no longer be sure of finding this znode by key, so we
+ * record it in the old_idx tree.
+ */
+ ins_clr_old_idx_znode(c, znode);
+
+ zn = kzalloc(c->max_znode_sz, GFP_NOFS);
+ if (!zn)
+ return -ENOMEM;
+ zn->parent = zp;
+ zn->level = znode->level;
+
+ /* Decide where to split */
+ if (znode->level == 0 && key_type(c, key) == UBIFS_DATA_KEY) {
+ /* Try not to split consecutive data keys */
+ if (n == c->fanout) {
+ key1 = &znode->zbranch[n - 1].key;
+ if (key_inum(c, key1) == key_inum(c, key) &&
+ key_type(c, key1) == UBIFS_DATA_KEY)
+ appending = 1;
+ } else
+ goto check_split;
+ } else if (appending && n != c->fanout) {
+ /* Try not to split consecutive data keys */
+ appending = 0;
+check_split:
+ if (n >= (c->fanout + 1) / 2) {
+ key1 = &znode->zbranch[0].key;
+ if (key_inum(c, key1) == key_inum(c, key) &&
+ key_type(c, key1) == UBIFS_DATA_KEY) {
+ key1 = &znode->zbranch[n].key;
+ if (key_inum(c, key1) != key_inum(c, key) ||
+ key_type(c, key1) != UBIFS_DATA_KEY) {
+ keep = n;
+ move = c->fanout - keep;
+ zi = znode;
+ goto do_split;
+ }
+ }
+ }
+ }
+
+ if (appending) {
+ keep = c->fanout;
+ move = 0;
+ } else {
+ keep = (c->fanout + 1) / 2;
+ move = c->fanout - keep;
+ }
+
+ /*
+ * Although we don't at present, we could look at the neighbors and see
+ * if we can move some zbranches there.
+ */
+
+ if (n < keep) {
+ /* Insert into existing znode */
+ zi = znode;
+ move += 1;
+ keep -= 1;
+ } else {
+ /* Insert into new znode */
+ zi = zn;
+ n -= keep;
+ /* Re-parent */
+ if (zn->level != 0)
+ zbr->znode->parent = zn;
+ }
+
+do_split:
+
+ __set_bit(DIRTY_ZNODE, &zn->flags);
+ atomic_long_inc(&c->dirty_zn_cnt);
+
+ zn->child_cnt = move;
+ znode->child_cnt = keep;
+
+ dbg_tnc("moving %d, keeping %d", move, keep);
+
+ /* Move zbranch */
+ for (i = 0; i < move; i++) {
+ zn->zbranch[i] = znode->zbranch[keep + i];
+ /* Re-parent */
+ if (zn->level != 0)
+ if (zn->zbranch[i].znode) {
+ zn->zbranch[i].znode->parent = zn;
+ zn->zbranch[i].znode->iip = i;
+ }
+ }
+
+ /* Insert new key and branch */
+ dbg_tnck(key, "inserting at %d level %d, key ", n, zn->level);
+
+ insert_zbranch(zi, zbr, n);
+
+ /* Insert new znode (produced by spitting) into the parent */
+ if (zp) {
+ if (n == 0 && zi == znode && znode->iip == 0)
+ correct_parent_keys(c, znode);
+
+ /* Locate insertion point */
+ n = znode->iip + 1;
+
+ /* Tail recursion */
+ zbr->key = zn->zbranch[0].key;
+ zbr->znode = zn;
+ zbr->lnum = 0;
+ zbr->offs = 0;
+ zbr->len = 0;
+ znode = zp;
+
+ goto again;
+ }
+
+ /* We have to split root znode */
+ dbg_tnc("creating new zroot at level %d", znode->level + 1);
+
+ zi = kzalloc(c->max_znode_sz, GFP_NOFS);
+ if (!zi)
+ return -ENOMEM;
+
+ zi->child_cnt = 2;
+ zi->level = znode->level + 1;
+
+ __set_bit(DIRTY_ZNODE, &zi->flags);
+ atomic_long_inc(&c->dirty_zn_cnt);
+
+ zi->zbranch[0].key = znode->zbranch[0].key;
+ zi->zbranch[0].znode = znode;
+ zi->zbranch[0].lnum = c->zroot.lnum;
+ zi->zbranch[0].offs = c->zroot.offs;
+ zi->zbranch[0].len = c->zroot.len;
+ zi->zbranch[1].key = zn->zbranch[0].key;
+ zi->zbranch[1].znode = zn;
+
+ c->zroot.lnum = 0;
+ c->zroot.offs = 0;
+ c->zroot.len = 0;
+ c->zroot.znode = zi;
+
+ zn->parent = zi;
+ zn->iip = 1;
+ znode->parent = zi;
+ znode->iip = 0;
+
+ return 0;
+}
+
+/**
+ * ubifs_tnc_add - add a node to TNC.
+ * @c: UBIFS file-system description object
+ * @key: key to add
+ * @lnum: LEB number of node
+ * @offs: node offset
+ * @len: node length
+ *
+ * This function adds a node with key @key to TNC. The node may be new or it may
+ * obsolete some existing one. Returns %0 on success or negative error code on
+ * failure.
+ */
+int ubifs_tnc_add(struct ubifs_info *c, const union ubifs_key *key, int lnum,
+ int offs, int len)
+{
+ int found, n, err = 0;
+ struct ubifs_znode *znode;
+
+ mutex_lock(&c->tnc_mutex);
+ dbg_tnck(key, "%d:%d, len %d, key ", lnum, offs, len);
+ found = lookup_level0_dirty(c, key, &znode, &n);
+ if (!found) {
+ struct ubifs_zbranch zbr;
+
+ zbr.znode = NULL;
+ zbr.lnum = lnum;
+ zbr.offs = offs;
+ zbr.len = len;
+ key_copy(c, key, &zbr.key);
+ err = tnc_insert(c, znode, &zbr, n + 1);
+ } else if (found == 1) {
+ struct ubifs_zbranch *zbr = &znode->zbranch[n];
+
+ lnc_free(zbr);
+ err = ubifs_add_dirt(c, zbr->lnum, zbr->len);
+ zbr->lnum = lnum;
+ zbr->offs = offs;
+ zbr->len = len;
+ } else
+ err = found;
+ if (!err)
+ err = dbg_check_tnc(c, 0);
+ mutex_unlock(&c->tnc_mutex);
+
+ return err;
+}
+
+/**
+ * ubifs_tnc_replace - replace a node in the TNC only if the old node is found.
+ * @c: UBIFS file-system description object
+ * @key: key to add
+ * @old_lnum: LEB number of old node
+ * @old_offs: old node offset
+ * @lnum: LEB number of node
+ * @offs: node offset
+ * @len: node length
+ *
+ * This function replaces a node with key @key in the TNC only if the old node
+ * is found. This function is called by garbage collection when node are moved.
+ * Returns %0 on success or negative error code on failure.
+ */
+int ubifs_tnc_replace(struct ubifs_info *c, const union ubifs_key *key,
+ int old_lnum, int old_offs, int lnum, int offs, int len)
+{
+ int found, n, err = 0;
+ struct ubifs_znode *znode;
+
+ mutex_lock(&c->tnc_mutex);
+ dbg_tnck(key, "old LEB %d:%d, new LEB %d:%d, len %d, key ", old_lnum,
+ old_offs, lnum, offs, len);
+ found = lookup_level0_dirty(c, key, &znode, &n);
+ if (found < 0) {
+ err = found;
+ goto out_unlock;
+ }
+
+ if (found == 1) {
+ struct ubifs_zbranch *zbr = &znode->zbranch[n];
+
+ found = 0;
+ if (zbr->lnum == old_lnum && zbr->offs == old_offs) {
+ lnc_free(zbr);
+ err = ubifs_add_dirt(c, zbr->lnum, zbr->len);
+ if (err)
+ goto out_unlock;
+ zbr->lnum = lnum;
+ zbr->offs = offs;
+ zbr->len = len;
+ found = 1;
+ } else if (is_hash_key(c, key)) {
+ found = resolve_collision_directly(c, key, &znode, &n,
+ old_lnum, old_offs);
+ dbg_tnc("rc returned %d, znode %p, n %d, LEB %d:%d",
+ found, znode, n, old_lnum, old_offs);
+ if (found < 0) {
+ err = found;
+ goto out_unlock;
+ }
+
+ if (found) {
+ /* Ensure the znode is dirtied */
+ if (znode->cnext || !ubifs_zn_dirty(znode)) {
+ znode = dirty_cow_bottom_up(c, znode);
+ if (IS_ERR(znode)) {
+ err = PTR_ERR(znode);
+ goto out_unlock;
+ }
+ }
+ zbr = &znode->zbranch[n];
+ lnc_free(zbr);
+ err = ubifs_add_dirt(c, zbr->lnum,
+ zbr->len);
+ if (err)
+ goto out_unlock;
+ zbr->lnum = lnum;
+ zbr->offs = offs;
+ zbr->len = len;
+ }
+ }
+ }
+
+ if (!found)
+ err = ubifs_add_dirt(c, lnum, len);
+
+ if (!err)
+ err = dbg_check_tnc(c, 0);
+
+out_unlock:
+ mutex_unlock(&c->tnc_mutex);
+ return err;
+}
+
+/**
+ * ubifs_tnc_add_nm - add a "hashed" node to TNC.
+ * @c: UBIFS file-system description object
+ * @key: key to add
+ * @lnum: LEB number of node
+ * @offs: node offset
+ * @len: node length
+ * @nm: node name
+ *
+ * This is the same as 'ubifs_tnc_add()' but it should be used with keys which
+ * may have collisions, like directory entry keys.
+ */
+int ubifs_tnc_add_nm(struct ubifs_info *c, const union ubifs_key *key,
+ int lnum, int offs, int len, const struct qstr *nm)
+{
+ int found, n, err = 0;
+ struct ubifs_znode *znode;
+
+ mutex_lock(&c->tnc_mutex);
+ dbg_tnck(key, "LEB %d:%d, name '%.*s', key ",
+ lnum, offs, nm->len, nm->name);
+ found = lookup_level0_dirty(c, key, &znode, &n);
+ if (found < 0) {
+ err = found;
+ goto out_unlock;
+ }
+
+ if (found == 1) {
+ if (c->replaying)
+ found = fallible_resolve_collision(c, key, &znode, &n,
+ nm, 1);
+ else
+ found = resolve_collision(c, key, &znode, &n, nm);
+ dbg_tnc("rc returned %d, znode %p, n %d", found, znode, n);
+ if (found < 0) {
+ err = found;
+ goto out_unlock;
+ }
+
+ /* Ensure the znode is dirtied */
+ if (znode->cnext || !ubifs_zn_dirty(znode)) {
+ znode = dirty_cow_bottom_up(c, znode);
+ if (IS_ERR(znode)) {
+ err = PTR_ERR(znode);
+ goto out_unlock;
+ }
+ }
+
+ if (found == 1) {
+ struct ubifs_zbranch *zbr = &znode->zbranch[n];
+
+ lnc_free(zbr);
+ err = ubifs_add_dirt(c, zbr->lnum, zbr->len);
+ zbr->lnum = lnum;
+ zbr->offs = offs;
+ zbr->len = len;
+ goto out_unlock;
+ }
+ }
+
+ if (!found) {
+ struct ubifs_zbranch zbr;
+
+ zbr.znode = NULL;
+ zbr.lnum = lnum;
+ zbr.offs = offs;
+ zbr.len = len;
+ key_copy(c, key, &zbr.key);
+ err = tnc_insert(c, znode, &zbr, n + 1);
+ if (err)
+ goto out_unlock;
+ if (c->replaying) {
+ /*
+ * We did not find it in the index so there may be a
+ * dangling branch still in the index. So we remove it
+ * by passing 'ubifs_tnc_remove_nm()' the same key but
+ * an unmatchable name.
+ */
+ struct qstr noname = { .name = "" };
+
+ err = dbg_check_tnc(c, 0);
+ mutex_unlock(&c->tnc_mutex);
+ if (err)
+ return err;
+ return ubifs_tnc_remove_nm(c, key, &noname);
+ }
+ }
+
+out_unlock:
+ if (!err)
+ err = dbg_check_tnc(c, 0);
+ mutex_unlock(&c->tnc_mutex);
+ return err;
+}
+
+/**
+ * tnc_delete - delete a znode form TNC.
+ * @c: UBIFS file-system description object
+ * @znode: znode to delete from
+ * @n: zbranch slot number to delete
+ *
+ * This function deletes a leaf node from @n-th slot of @znode. Returns zero in
+ * case of success and a negative error code in case of failure.
+ */
+static int tnc_delete(struct ubifs_info *c, struct ubifs_znode *znode, int n)
+{
+ struct ubifs_zbranch *zbr;
+ struct ubifs_znode *zp;
+ int i, err;
+
+ /* Delete without merge for now */
+ ubifs_assert(znode->level == 0);
+ ubifs_assert(n >= 0 && n < c->fanout);
+ dbg_tnck(&znode->zbranch[n].key, "deleting key ");
+
+ zbr = &znode->zbranch[n];
+ lnc_free(zbr);
+
+ err = ubifs_add_dirt(c, zbr->lnum, zbr->len);
+ if (err) {
+ ubifs_dump_znode(c, znode);
+ return err;
+ }
+
+ /* We do not "gap" zbranch slots */
+ for (i = n; i < znode->child_cnt - 1; i++)
+ znode->zbranch[i] = znode->zbranch[i + 1];
+ znode->child_cnt -= 1;
+
+ if (znode->child_cnt > 0)
+ return 0;
+
+ /*
+ * This was the last zbranch, we have to delete this znode from the
+ * parent.
+ */
+
+ do {
+ ubifs_assert(!ubifs_zn_obsolete(znode));
+ ubifs_assert(ubifs_zn_dirty(znode));
+
+ zp = znode->parent;
+ n = znode->iip;
+
+ atomic_long_dec(&c->dirty_zn_cnt);
+
+ err = insert_old_idx_znode(c, znode);
+ if (err)
+ return err;
+
+ if (znode->cnext) {
+ __set_bit(OBSOLETE_ZNODE, &znode->flags);
+ atomic_long_inc(&c->clean_zn_cnt);
+ atomic_long_inc(&ubifs_clean_zn_cnt);
+ } else
+ kfree(znode);
+ znode = zp;
+ } while (znode->child_cnt == 1); /* while removing last child */
+
+ /* Remove from znode, entry n - 1 */
+ znode->child_cnt -= 1;
+ ubifs_assert(znode->level != 0);
+ for (i = n; i < znode->child_cnt; i++) {
+ znode->zbranch[i] = znode->zbranch[i + 1];
+ if (znode->zbranch[i].znode)
+ znode->zbranch[i].znode->iip = i;
+ }
+
+ /*
+ * If this is the root and it has only 1 child then
+ * collapse the tree.
+ */
+ if (!znode->parent) {
+ while (znode->child_cnt == 1 && znode->level != 0) {
+ zp = znode;
+ zbr = &znode->zbranch[0];
+ znode = get_znode(c, znode, 0);
+ if (IS_ERR(znode))
+ return PTR_ERR(znode);
+ znode = dirty_cow_znode(c, zbr);
+ if (IS_ERR(znode))
+ return PTR_ERR(znode);
+ znode->parent = NULL;
+ znode->iip = 0;
+ if (c->zroot.len) {
+ err = insert_old_idx(c, c->zroot.lnum,
+ c->zroot.offs);
+ if (err)
+ return err;
+ }
+ c->zroot.lnum = zbr->lnum;
+ c->zroot.offs = zbr->offs;
+ c->zroot.len = zbr->len;
+ c->zroot.znode = znode;
+ ubifs_assert(!ubifs_zn_obsolete(zp));
+ ubifs_assert(ubifs_zn_dirty(zp));
+ atomic_long_dec(&c->dirty_zn_cnt);
+
+ if (zp->cnext) {
+ __set_bit(OBSOLETE_ZNODE, &zp->flags);
+ atomic_long_inc(&c->clean_zn_cnt);
+ atomic_long_inc(&ubifs_clean_zn_cnt);
+ } else
+ kfree(zp);
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * ubifs_tnc_remove - remove an index entry of a node.
+ * @c: UBIFS file-system description object
+ * @key: key of node
+ *
+ * Returns %0 on success or negative error code on failure.
+ */
+int ubifs_tnc_remove(struct ubifs_info *c, const union ubifs_key *key)
+{
+ int found, n, err = 0;
+ struct ubifs_znode *znode;
+
+ mutex_lock(&c->tnc_mutex);
+ dbg_tnck(key, "key ");
+ found = lookup_level0_dirty(c, key, &znode, &n);
+ if (found < 0) {
+ err = found;
+ goto out_unlock;
+ }
+ if (found == 1)
+ err = tnc_delete(c, znode, n);
+ if (!err)
+ err = dbg_check_tnc(c, 0);
+
+out_unlock:
+ mutex_unlock(&c->tnc_mutex);
+ return err;
+}
+
+/**
+ * ubifs_tnc_remove_nm - remove an index entry for a "hashed" node.
+ * @c: UBIFS file-system description object
+ * @key: key of node
+ * @nm: directory entry name
+ *
+ * Returns %0 on success or negative error code on failure.
+ */
+int ubifs_tnc_remove_nm(struct ubifs_info *c, const union ubifs_key *key,
+ const struct qstr *nm)
+{
+ int n, err;
+ struct ubifs_znode *znode;
+
+ mutex_lock(&c->tnc_mutex);
+ dbg_tnck(key, "%.*s, key ", nm->len, nm->name);
+ err = lookup_level0_dirty(c, key, &znode, &n);
+ if (err < 0)
+ goto out_unlock;
+
+ if (err) {
+ if (c->replaying)
+ err = fallible_resolve_collision(c, key, &znode, &n,
+ nm, 0);
+ else
+ err = resolve_collision(c, key, &znode, &n, nm);
+ dbg_tnc("rc returned %d, znode %p, n %d", err, znode, n);
+ if (err < 0)
+ goto out_unlock;
+ if (err) {
+ /* Ensure the znode is dirtied */
+ if (znode->cnext || !ubifs_zn_dirty(znode)) {
+ znode = dirty_cow_bottom_up(c, znode);
+ if (IS_ERR(znode)) {
+ err = PTR_ERR(znode);
+ goto out_unlock;
+ }
+ }
+ err = tnc_delete(c, znode, n);
+ }
+ }
+
+out_unlock:
+ if (!err)
+ err = dbg_check_tnc(c, 0);
+ mutex_unlock(&c->tnc_mutex);
+ return err;
+}
+
+/**
+ * key_in_range - determine if a key falls within a range of keys.
+ * @c: UBIFS file-system description object
+ * @key: key to check
+ * @from_key: lowest key in range
+ * @to_key: highest key in range
+ *
+ * This function returns %1 if the key is in range and %0 otherwise.
+ */
+static int key_in_range(struct ubifs_info *c, union ubifs_key *key,
+ union ubifs_key *from_key, union ubifs_key *to_key)
+{
+ if (keys_cmp(c, key, from_key) < 0)
+ return 0;
+ if (keys_cmp(c, key, to_key) > 0)
+ return 0;
+ return 1;
+}
+
+/**
+ * ubifs_tnc_remove_range - remove index entries in range.
+ * @c: UBIFS file-system description object
+ * @from_key: lowest key to remove
+ * @to_key: highest key to remove
+ *
+ * This function removes index entries starting at @from_key and ending at
+ * @to_key. This function returns zero in case of success and a negative error
+ * code in case of failure.
+ */
+int ubifs_tnc_remove_range(struct ubifs_info *c, union ubifs_key *from_key,
+ union ubifs_key *to_key)
+{
+ int i, n, k, err = 0;
+ struct ubifs_znode *znode;
+ union ubifs_key *key;
+
+ mutex_lock(&c->tnc_mutex);
+ while (1) {
+ /* Find first level 0 znode that contains keys to remove */
+ err = ubifs_lookup_level0(c, from_key, &znode, &n);
+ if (err < 0)
+ goto out_unlock;
+
+ if (err)
+ key = from_key;
+ else {
+ err = tnc_next(c, &znode, &n);
+ if (err == -ENOENT) {
+ err = 0;
+ goto out_unlock;
+ }
+ if (err < 0)
+ goto out_unlock;
+ key = &znode->zbranch[n].key;
+ if (!key_in_range(c, key, from_key, to_key)) {
+ err = 0;
+ goto out_unlock;
+ }
+ }
+
+ /* Ensure the znode is dirtied */
+ if (znode->cnext || !ubifs_zn_dirty(znode)) {
+ znode = dirty_cow_bottom_up(c, znode);
+ if (IS_ERR(znode)) {
+ err = PTR_ERR(znode);
+ goto out_unlock;
+ }
+ }
+
+ /* Remove all keys in range except the first */
+ for (i = n + 1, k = 0; i < znode->child_cnt; i++, k++) {
+ key = &znode->zbranch[i].key;
+ if (!key_in_range(c, key, from_key, to_key))
+ break;
+ lnc_free(&znode->zbranch[i]);
+ err = ubifs_add_dirt(c, znode->zbranch[i].lnum,
+ znode->zbranch[i].len);
+ if (err) {
+ ubifs_dump_znode(c, znode);
+ goto out_unlock;
+ }
+ dbg_tnck(key, "removing key ");
+ }
+ if (k) {
+ for (i = n + 1 + k; i < znode->child_cnt; i++)
+ znode->zbranch[i - k] = znode->zbranch[i];
+ znode->child_cnt -= k;
+ }
+
+ /* Now delete the first */
+ err = tnc_delete(c, znode, n);
+ if (err)
+ goto out_unlock;
+ }
+
+out_unlock:
+ if (!err)
+ err = dbg_check_tnc(c, 0);
+ mutex_unlock(&c->tnc_mutex);
+ return err;
+}
+
+/**
+ * ubifs_tnc_remove_ino - remove an inode from TNC.
+ * @c: UBIFS file-system description object
+ * @inum: inode number to remove
+ *
+ * This function remove inode @inum and all the extended attributes associated
+ * with the anode from TNC and returns zero in case of success or a negative
+ * error code in case of failure.
+ */
+int ubifs_tnc_remove_ino(struct ubifs_info *c, ino_t inum)
+{
+ union ubifs_key key1, key2;
+ struct ubifs_dent_node *xent, *pxent = NULL;
+ struct qstr nm = { .name = NULL };
+
+ dbg_tnc("ino %lu", (unsigned long)inum);
+
+ /*
+ * Walk all extended attribute entries and remove them together with
+ * corresponding extended attribute inodes.
+ */
+ lowest_xent_key(c, &key1, inum);
+ while (1) {
+ ino_t xattr_inum;
+ int err;
+
+ xent = ubifs_tnc_next_ent(c, &key1, &nm);
+ if (IS_ERR(xent)) {
+ err = PTR_ERR(xent);
+ if (err == -ENOENT)
+ break;
+ return err;
+ }
+
+ xattr_inum = le64_to_cpu(xent->inum);
+ dbg_tnc("xent '%s', ino %lu", xent->name,
+ (unsigned long)xattr_inum);
+
+ nm.name = xent->name;
+ nm.len = le16_to_cpu(xent->nlen);
+ err = ubifs_tnc_remove_nm(c, &key1, &nm);
+ if (err) {
+ kfree(xent);
+ return err;
+ }
+
+ lowest_ino_key(c, &key1, xattr_inum);
+ highest_ino_key(c, &key2, xattr_inum);
+ err = ubifs_tnc_remove_range(c, &key1, &key2);
+ if (err) {
+ kfree(xent);
+ return err;
+ }
+
+ kfree(pxent);
+ pxent = xent;
+ key_read(c, &xent->key, &key1);
+ }
+
+ kfree(pxent);
+ lowest_ino_key(c, &key1, inum);
+ highest_ino_key(c, &key2, inum);
+
+ return ubifs_tnc_remove_range(c, &key1, &key2);
+}
+
+/**
+ * ubifs_tnc_next_ent - walk directory or extended attribute entries.
+ * @c: UBIFS file-system description object
+ * @key: key of last entry
+ * @nm: name of last entry found or %NULL
+ *
+ * This function finds and reads the next directory or extended attribute entry
+ * after the given key (@key) if there is one. @nm is used to resolve
+ * collisions.
+ *
+ * If the name of the current entry is not known and only the key is known,
+ * @nm->name has to be %NULL. In this case the semantics of this function is a
+ * little bit different and it returns the entry corresponding to this key, not
+ * the next one. If the key was not found, the closest "right" entry is
+ * returned.
+ *
+ * If the fist entry has to be found, @key has to contain the lowest possible
+ * key value for this inode and @name has to be %NULL.
+ *
+ * This function returns the found directory or extended attribute entry node
+ * in case of success, %-ENOENT is returned if no entry was found, and a
+ * negative error code is returned in case of failure.
+ */
+struct ubifs_dent_node *ubifs_tnc_next_ent(struct ubifs_info *c,
+ union ubifs_key *key,
+ const struct qstr *nm)
+{
+ int n, err, type = key_type(c, key);
+ struct ubifs_znode *znode;
+ struct ubifs_dent_node *dent;
+ struct ubifs_zbranch *zbr;
+ union ubifs_key *dkey;
+
+ dbg_tnck(key, "%s ", nm->name ? (char *)nm->name : "(lowest)");
+ ubifs_assert(is_hash_key(c, key));
+
+ mutex_lock(&c->tnc_mutex);
+ err = ubifs_lookup_level0(c, key, &znode, &n);
+ if (unlikely(err < 0))
+ goto out_unlock;
+
+ if (nm->name) {
+ if (err) {
+ /* Handle collisions */
+ err = resolve_collision(c, key, &znode, &n, nm);
+ dbg_tnc("rc returned %d, znode %p, n %d",
+ err, znode, n);
+ if (unlikely(err < 0))
+ goto out_unlock;
+ }
+
+ /* Now find next entry */
+ err = tnc_next(c, &znode, &n);
+ if (unlikely(err))
+ goto out_unlock;
+ } else {
+ /*
+ * The full name of the entry was not given, in which case the
+ * behavior of this function is a little different and it
+ * returns current entry, not the next one.
+ */
+ if (!err) {
+ /*
+ * However, the given key does not exist in the TNC
+ * tree and @znode/@n variables contain the closest
+ * "preceding" element. Switch to the next one.
+ */
+ err = tnc_next(c, &znode, &n);
+ if (err)
+ goto out_unlock;
+ }
+ }
+
+ zbr = &znode->zbranch[n];
+ dent = kmalloc(zbr->len, GFP_NOFS);
+ if (unlikely(!dent)) {
+ err = -ENOMEM;
+ goto out_unlock;
+ }
+
+ /*
+ * The above 'tnc_next()' call could lead us to the next inode, check
+ * this.
+ */
+ dkey = &zbr->key;
+ if (key_inum(c, dkey) != key_inum(c, key) ||
+ key_type(c, dkey) != type) {
+ err = -ENOENT;
+ goto out_free;
+ }
+
+ err = tnc_read_node_nm(c, zbr, dent);
+ if (unlikely(err))
+ goto out_free;
+
+ mutex_unlock(&c->tnc_mutex);
+ return dent;
+
+out_free:
+ kfree(dent);
+out_unlock:
+ mutex_unlock(&c->tnc_mutex);
+ return ERR_PTR(err);
+}
+
+/**
+ * tnc_destroy_cnext - destroy left-over obsolete znodes from a failed commit.
+ * @c: UBIFS file-system description object
+ *
+ * Destroy left-over obsolete znodes from a failed commit.
+ */
+static void tnc_destroy_cnext(struct ubifs_info *c)
+{
+ struct ubifs_znode *cnext;
+
+ if (!c->cnext)
+ return;
+ ubifs_assert(c->cmt_state == COMMIT_BROKEN);
+ cnext = c->cnext;
+ do {
+ struct ubifs_znode *znode = cnext;
+
+ cnext = cnext->cnext;
+ if (ubifs_zn_obsolete(znode))
+ kfree(znode);
+ } while (cnext && cnext != c->cnext);
+}
+
+/**
+ * ubifs_tnc_close - close TNC subsystem and free all related resources.
+ * @c: UBIFS file-system description object
+ */
+void ubifs_tnc_close(struct ubifs_info *c)
+{
+ tnc_destroy_cnext(c);
+ if (c->zroot.znode) {
+ long n, freed;
+
+ n = atomic_long_read(&c->clean_zn_cnt);
+ freed = ubifs_destroy_tnc_subtree(c->zroot.znode);
+ ubifs_assert(freed == n);
+ atomic_long_sub(n, &ubifs_clean_zn_cnt);
+ }
+ kfree(c->gap_lebs);
+ kfree(c->ilebs);
+ destroy_old_idx(c);
+}
+
+/**
+ * left_znode - get the znode to the left.
+ * @c: UBIFS file-system description object
+ * @znode: znode
+ *
+ * This function returns a pointer to the znode to the left of @znode or NULL if
+ * there is not one. A negative error code is returned on failure.
+ */
+static struct ubifs_znode *left_znode(struct ubifs_info *c,
+ struct ubifs_znode *znode)
+{
+ int level = znode->level;
+
+ while (1) {
+ int n = znode->iip - 1;
+
+ /* Go up until we can go left */
+ znode = znode->parent;
+ if (!znode)
+ return NULL;
+ if (n >= 0) {
+ /* Now go down the rightmost branch to 'level' */
+ znode = get_znode(c, znode, n);
+ if (IS_ERR(znode))
+ return znode;
+ while (znode->level != level) {
+ n = znode->child_cnt - 1;
+ znode = get_znode(c, znode, n);
+ if (IS_ERR(znode))
+ return znode;
+ }
+ break;
+ }
+ }
+ return znode;
+}
+
+/**
+ * right_znode - get the znode to the right.
+ * @c: UBIFS file-system description object
+ * @znode: znode
+ *
+ * This function returns a pointer to the znode to the right of @znode or NULL
+ * if there is not one. A negative error code is returned on failure.
+ */
+static struct ubifs_znode *right_znode(struct ubifs_info *c,
+ struct ubifs_znode *znode)
+{
+ int level = znode->level;
+
+ while (1) {
+ int n = znode->iip + 1;
+
+ /* Go up until we can go right */
+ znode = znode->parent;
+ if (!znode)
+ return NULL;
+ if (n < znode->child_cnt) {
+ /* Now go down the leftmost branch to 'level' */
+ znode = get_znode(c, znode, n);
+ if (IS_ERR(znode))
+ return znode;
+ while (znode->level != level) {
+ znode = get_znode(c, znode, 0);
+ if (IS_ERR(znode))
+ return znode;
+ }
+ break;
+ }
+ }
+ return znode;
+}
+
+/**
+ * lookup_znode - find a particular indexing node from TNC.
+ * @c: UBIFS file-system description object
+ * @key: index node key to lookup
+ * @level: index node level
+ * @lnum: index node LEB number
+ * @offs: index node offset
+ *
+ * This function searches an indexing node by its first key @key and its
+ * address @lnum:@offs. It looks up the indexing tree by pulling all indexing
+ * nodes it traverses to TNC. This function is called for indexing nodes which
+ * were found on the media by scanning, for example when garbage-collecting or
+ * when doing in-the-gaps commit. This means that the indexing node which is
+ * looked for does not have to have exactly the same leftmost key @key, because
+ * the leftmost key may have been changed, in which case TNC will contain a
+ * dirty znode which still refers the same @lnum:@offs. This function is clever
+ * enough to recognize such indexing nodes.
+ *
+ * Note, if a znode was deleted or changed too much, then this function will
+ * not find it. For situations like this UBIFS has the old index RB-tree
+ * (indexed by @lnum:@offs).
+ *
+ * This function returns a pointer to the znode found or %NULL if it is not
+ * found. A negative error code is returned on failure.
+ */
+static struct ubifs_znode *lookup_znode(struct ubifs_info *c,
+ union ubifs_key *key, int level,
+ int lnum, int offs)
+{
+ struct ubifs_znode *znode, *zn;
+ int n, nn;
+
+ ubifs_assert(key_type(c, key) < UBIFS_INVALID_KEY);
+
+ /*
+ * The arguments have probably been read off flash, so don't assume
+ * they are valid.
+ */
+ if (level < 0)
+ return ERR_PTR(-EINVAL);
+
+ /* Get the root znode */
+ znode = c->zroot.znode;
+ if (!znode) {
+ znode = ubifs_load_znode(c, &c->zroot, NULL, 0);
+ if (IS_ERR(znode))
+ return znode;
+ }
+ /* Check if it is the one we are looking for */
+ if (c->zroot.lnum == lnum && c->zroot.offs == offs)
+ return znode;
+ /* Descend to the parent level i.e. (level + 1) */
+ if (level >= znode->level)
+ return NULL;
+ while (1) {
+ ubifs_search_zbranch(c, znode, key, &n);
+ if (n < 0) {
+ /*
+ * We reached a znode where the leftmost key is greater
+ * than the key we are searching for. This is the same
+ * situation as the one described in a huge comment at
+ * the end of the 'ubifs_lookup_level0()' function. And
+ * for exactly the same reasons we have to try to look
+ * left before giving up.
+ */
+ znode = left_znode(c, znode);
+ if (!znode)
+ return NULL;
+ if (IS_ERR(znode))
+ return znode;
+ ubifs_search_zbranch(c, znode, key, &n);
+ ubifs_assert(n >= 0);
+ }
+ if (znode->level == level + 1)
+ break;
+ znode = get_znode(c, znode, n);
+ if (IS_ERR(znode))
+ return znode;
+ }
+ /* Check if the child is the one we are looking for */
+ if (znode->zbranch[n].lnum == lnum && znode->zbranch[n].offs == offs)
+ return get_znode(c, znode, n);
+ /* If the key is unique, there is nowhere else to look */
+ if (!is_hash_key(c, key))
+ return NULL;
+ /*
+ * The key is not unique and so may be also in the znodes to either
+ * side.
+ */
+ zn = znode;
+ nn = n;
+ /* Look left */
+ while (1) {
+ /* Move one branch to the left */
+ if (n)
+ n -= 1;
+ else {
+ znode = left_znode(c, znode);
+ if (!znode)
+ break;
+ if (IS_ERR(znode))
+ return znode;
+ n = znode->child_cnt - 1;
+ }
+ /* Check it */
+ if (znode->zbranch[n].lnum == lnum &&
+ znode->zbranch[n].offs == offs)
+ return get_znode(c, znode, n);
+ /* Stop if the key is less than the one we are looking for */
+ if (keys_cmp(c, &znode->zbranch[n].key, key) < 0)
+ break;
+ }
+ /* Back to the middle */
+ znode = zn;
+ n = nn;
+ /* Look right */
+ while (1) {
+ /* Move one branch to the right */
+ if (++n >= znode->child_cnt) {
+ znode = right_znode(c, znode);
+ if (!znode)
+ break;
+ if (IS_ERR(znode))
+ return znode;
+ n = 0;
+ }
+ /* Check it */
+ if (znode->zbranch[n].lnum == lnum &&
+ znode->zbranch[n].offs == offs)
+ return get_znode(c, znode, n);
+ /* Stop if the key is greater than the one we are looking for */
+ if (keys_cmp(c, &znode->zbranch[n].key, key) > 0)
+ break;
+ }
+ return NULL;
+}
+
+/**
+ * is_idx_node_in_tnc - determine if an index node is in the TNC.
+ * @c: UBIFS file-system description object
+ * @key: key of index node
+ * @level: index node level
+ * @lnum: LEB number of index node
+ * @offs: offset of index node
+ *
+ * This function returns %0 if the index node is not referred to in the TNC, %1
+ * if the index node is referred to in the TNC and the corresponding znode is
+ * dirty, %2 if an index node is referred to in the TNC and the corresponding
+ * znode is clean, and a negative error code in case of failure.
+ *
+ * Note, the @key argument has to be the key of the first child. Also note,
+ * this function relies on the fact that 0:0 is never a valid LEB number and
+ * offset for a main-area node.
+ */
+int is_idx_node_in_tnc(struct ubifs_info *c, union ubifs_key *key, int level,
+ int lnum, int offs)
+{
+ struct ubifs_znode *znode;
+
+ znode = lookup_znode(c, key, level, lnum, offs);
+ if (!znode)
+ return 0;
+ if (IS_ERR(znode))
+ return PTR_ERR(znode);
+
+ return ubifs_zn_dirty(znode) ? 1 : 2;
+}
+
+/**
+ * is_leaf_node_in_tnc - determine if a non-indexing not is in the TNC.
+ * @c: UBIFS file-system description object
+ * @key: node key
+ * @lnum: node LEB number
+ * @offs: node offset
+ *
+ * This function returns %1 if the node is referred to in the TNC, %0 if it is
+ * not, and a negative error code in case of failure.
+ *
+ * Note, this function relies on the fact that 0:0 is never a valid LEB number
+ * and offset for a main-area node.
+ */
+static int is_leaf_node_in_tnc(struct ubifs_info *c, union ubifs_key *key,
+ int lnum, int offs)
+{
+ struct ubifs_zbranch *zbr;
+ struct ubifs_znode *znode, *zn;
+ int n, found, err, nn;
+ const int unique = !is_hash_key(c, key);
+
+ found = ubifs_lookup_level0(c, key, &znode, &n);
+ if (found < 0)
+ return found; /* Error code */
+ if (!found)
+ return 0;
+ zbr = &znode->zbranch[n];
+ if (lnum == zbr->lnum && offs == zbr->offs)
+ return 1; /* Found it */
+ if (unique)
+ return 0;
+ /*
+ * Because the key is not unique, we have to look left
+ * and right as well
+ */
+ zn = znode;
+ nn = n;
+ /* Look left */
+ while (1) {
+ err = tnc_prev(c, &znode, &n);
+ if (err == -ENOENT)
+ break;
+ if (err)
+ return err;
+ if (keys_cmp(c, key, &znode->zbranch[n].key))
+ break;
+ zbr = &znode->zbranch[n];
+ if (lnum == zbr->lnum && offs == zbr->offs)
+ return 1; /* Found it */
+ }
+ /* Look right */
+ znode = zn;
+ n = nn;
+ while (1) {
+ err = tnc_next(c, &znode, &n);
+ if (err) {
+ if (err == -ENOENT)
+ return 0;
+ return err;
+ }
+ if (keys_cmp(c, key, &znode->zbranch[n].key))
+ break;
+ zbr = &znode->zbranch[n];
+ if (lnum == zbr->lnum && offs == zbr->offs)
+ return 1; /* Found it */
+ }
+ return 0;
+}
+
+/**
+ * ubifs_tnc_has_node - determine whether a node is in the TNC.
+ * @c: UBIFS file-system description object
+ * @key: node key
+ * @level: index node level (if it is an index node)
+ * @lnum: node LEB number
+ * @offs: node offset
+ * @is_idx: non-zero if the node is an index node
+ *
+ * This function returns %1 if the node is in the TNC, %0 if it is not, and a
+ * negative error code in case of failure. For index nodes, @key has to be the
+ * key of the first child. An index node is considered to be in the TNC only if
+ * the corresponding znode is clean or has not been loaded.
+ */
+int ubifs_tnc_has_node(struct ubifs_info *c, union ubifs_key *key, int level,
+ int lnum, int offs, int is_idx)
+{
+ int err;
+
+ mutex_lock(&c->tnc_mutex);
+ if (is_idx) {
+ err = is_idx_node_in_tnc(c, key, level, lnum, offs);
+ if (err < 0)
+ goto out_unlock;
+ if (err == 1)
+ /* The index node was found but it was dirty */
+ err = 0;
+ else if (err == 2)
+ /* The index node was found and it was clean */
+ err = 1;
+ else
+ BUG_ON(err != 0);
+ } else
+ err = is_leaf_node_in_tnc(c, key, lnum, offs);
+
+out_unlock:
+ mutex_unlock(&c->tnc_mutex);
+ return err;
+}
+
+/**
+ * ubifs_dirty_idx_node - dirty an index node.
+ * @c: UBIFS file-system description object
+ * @key: index node key
+ * @level: index node level
+ * @lnum: index node LEB number
+ * @offs: index node offset
+ *
+ * This function loads and dirties an index node so that it can be garbage
+ * collected. The @key argument has to be the key of the first child. This
+ * function relies on the fact that 0:0 is never a valid LEB number and offset
+ * for a main-area node. Returns %0 on success and a negative error code on
+ * failure.
+ */
+int ubifs_dirty_idx_node(struct ubifs_info *c, union ubifs_key *key, int level,
+ int lnum, int offs)
+{
+ struct ubifs_znode *znode;
+ int err = 0;
+
+ mutex_lock(&c->tnc_mutex);
+ znode = lookup_znode(c, key, level, lnum, offs);
+ if (!znode)
+ goto out_unlock;
+ if (IS_ERR(znode)) {
+ err = PTR_ERR(znode);
+ goto out_unlock;
+ }
+ znode = dirty_cow_bottom_up(c, znode);
+ if (IS_ERR(znode)) {
+ err = PTR_ERR(znode);
+ goto out_unlock;
+ }
+
+out_unlock:
+ mutex_unlock(&c->tnc_mutex);
+ return err;
+}
+
+/**
+ * dbg_check_inode_size - check if inode size is correct.
+ * @c: UBIFS file-system description object
+ * @inum: inode number
+ * @size: inode size
+ *
+ * This function makes sure that the inode size (@size) is correct and it does
+ * not have any pages beyond @size. Returns zero if the inode is OK, %-EINVAL
+ * if it has a data page beyond @size, and other negative error code in case of
+ * other errors.
+ */
+int dbg_check_inode_size(struct ubifs_info *c, const struct inode *inode,
+ loff_t size)
+{
+ int err, n;
+ union ubifs_key from_key, to_key, *key;
+ struct ubifs_znode *znode;
+ unsigned int block;
+
+ if (!S_ISREG(inode->i_mode))
+ return 0;
+ if (!dbg_is_chk_gen(c))
+ return 0;
+
+ block = (size + UBIFS_BLOCK_SIZE - 1) >> UBIFS_BLOCK_SHIFT;
+ data_key_init(c, &from_key, inode->i_ino, block);
+ highest_data_key(c, &to_key, inode->i_ino);
+
+ mutex_lock(&c->tnc_mutex);
+ err = ubifs_lookup_level0(c, &from_key, &znode, &n);
+ if (err < 0)
+ goto out_unlock;
+
+ if (err) {
+ key = &from_key;
+ goto out_dump;
+ }
+
+ err = tnc_next(c, &znode, &n);
+ if (err == -ENOENT) {
+ err = 0;
+ goto out_unlock;
+ }
+ if (err < 0)
+ goto out_unlock;
+
+ ubifs_assert(err == 0);
+ key = &znode->zbranch[n].key;
+ if (!key_in_range(c, key, &from_key, &to_key))
+ goto out_unlock;
+
+out_dump:
+ block = key_block(c, key);
+ ubifs_err(c, "inode %lu has size %lld, but there are data at offset %lld",
+ (unsigned long)inode->i_ino, size,
+ ((loff_t)block) << UBIFS_BLOCK_SHIFT);
+ mutex_unlock(&c->tnc_mutex);
+ ubifs_dump_inode(c, inode);
+ dump_stack();
+ return -EINVAL;
+
+out_unlock:
+ mutex_unlock(&c->tnc_mutex);
+ return err;
+}
diff --git a/roms/u-boot/fs/ubifs/tnc_misc.c b/roms/u-boot/fs/ubifs/tnc_misc.c
new file mode 100644
index 000000000..e4cd785b9
--- /dev/null
+++ b/roms/u-boot/fs/ubifs/tnc_misc.c
@@ -0,0 +1,487 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ *
+ * Authors: Adrian Hunter
+ * Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * This file contains miscelanious TNC-related functions shared betweend
+ * different files. This file does not form any logically separate TNC
+ * sub-system. The file was created because there is a lot of TNC code and
+ * putting it all in one file would make that file too big and unreadable.
+ */
+
+#ifdef __UBOOT__
+#include <log.h>
+#include <dm/devres.h>
+#include <linux/err.h>
+#endif
+#include "ubifs.h"
+
+/**
+ * ubifs_tnc_levelorder_next - next TNC tree element in levelorder traversal.
+ * @zr: root of the subtree to traverse
+ * @znode: previous znode
+ *
+ * This function implements levelorder TNC traversal. The LNC is ignored.
+ * Returns the next element or %NULL if @znode is already the last one.
+ */
+struct ubifs_znode *ubifs_tnc_levelorder_next(struct ubifs_znode *zr,
+ struct ubifs_znode *znode)
+{
+ int level, iip, level_search = 0;
+ struct ubifs_znode *zn;
+
+ ubifs_assert(zr);
+
+ if (unlikely(!znode))
+ return zr;
+
+ if (unlikely(znode == zr)) {
+ if (znode->level == 0)
+ return NULL;
+ return ubifs_tnc_find_child(zr, 0);
+ }
+
+ level = znode->level;
+
+ iip = znode->iip;
+ while (1) {
+ ubifs_assert(znode->level <= zr->level);
+
+ /*
+ * First walk up until there is a znode with next branch to
+ * look at.
+ */
+ while (znode->parent != zr && iip >= znode->parent->child_cnt) {
+ znode = znode->parent;
+ iip = znode->iip;
+ }
+
+ if (unlikely(znode->parent == zr &&
+ iip >= znode->parent->child_cnt)) {
+ /* This level is done, switch to the lower one */
+ level -= 1;
+ if (level_search || level < 0)
+ /*
+ * We were already looking for znode at lower
+ * level ('level_search'). As we are here
+ * again, it just does not exist. Or all levels
+ * were finished ('level < 0').
+ */
+ return NULL;
+
+ level_search = 1;
+ iip = -1;
+ znode = ubifs_tnc_find_child(zr, 0);
+ ubifs_assert(znode);
+ }
+
+ /* Switch to the next index */
+ zn = ubifs_tnc_find_child(znode->parent, iip + 1);
+ if (!zn) {
+ /* No more children to look at, we have walk up */
+ iip = znode->parent->child_cnt;
+ continue;
+ }
+
+ /* Walk back down to the level we came from ('level') */
+ while (zn->level != level) {
+ znode = zn;
+ zn = ubifs_tnc_find_child(zn, 0);
+ if (!zn) {
+ /*
+ * This path is not too deep so it does not
+ * reach 'level'. Try next path.
+ */
+ iip = znode->iip;
+ break;
+ }
+ }
+
+ if (zn) {
+ ubifs_assert(zn->level >= 0);
+ return zn;
+ }
+ }
+}
+
+/**
+ * ubifs_search_zbranch - search znode branch.
+ * @c: UBIFS file-system description object
+ * @znode: znode to search in
+ * @key: key to search for
+ * @n: znode branch slot number is returned here
+ *
+ * This is a helper function which search branch with key @key in @znode using
+ * binary search. The result of the search may be:
+ * o exact match, then %1 is returned, and the slot number of the branch is
+ * stored in @n;
+ * o no exact match, then %0 is returned and the slot number of the left
+ * closest branch is returned in @n; the slot if all keys in this znode are
+ * greater than @key, then %-1 is returned in @n.
+ */
+int ubifs_search_zbranch(const struct ubifs_info *c,
+ const struct ubifs_znode *znode,
+ const union ubifs_key *key, int *n)
+{
+ int beg = 0, end = znode->child_cnt, uninitialized_var(mid);
+ int uninitialized_var(cmp);
+ const struct ubifs_zbranch *zbr = &znode->zbranch[0];
+
+ ubifs_assert(end > beg);
+
+ while (end > beg) {
+ mid = (beg + end) >> 1;
+ cmp = keys_cmp(c, key, &zbr[mid].key);
+ if (cmp > 0)
+ beg = mid + 1;
+ else if (cmp < 0)
+ end = mid;
+ else {
+ *n = mid;
+ return 1;
+ }
+ }
+
+ *n = end - 1;
+
+ /* The insert point is after *n */
+ ubifs_assert(*n >= -1 && *n < znode->child_cnt);
+ if (*n == -1)
+ ubifs_assert(keys_cmp(c, key, &zbr[0].key) < 0);
+ else
+ ubifs_assert(keys_cmp(c, key, &zbr[*n].key) > 0);
+ if (*n + 1 < znode->child_cnt)
+ ubifs_assert(keys_cmp(c, key, &zbr[*n + 1].key) < 0);
+
+ return 0;
+}
+
+/**
+ * ubifs_tnc_postorder_first - find first znode to do postorder tree traversal.
+ * @znode: znode to start at (root of the sub-tree to traverse)
+ *
+ * Find the lowest leftmost znode in a subtree of the TNC tree. The LNC is
+ * ignored.
+ */
+struct ubifs_znode *ubifs_tnc_postorder_first(struct ubifs_znode *znode)
+{
+ if (unlikely(!znode))
+ return NULL;
+
+ while (znode->level > 0) {
+ struct ubifs_znode *child;
+
+ child = ubifs_tnc_find_child(znode, 0);
+ if (!child)
+ return znode;
+ znode = child;
+ }
+
+ return znode;
+}
+
+/**
+ * ubifs_tnc_postorder_next - next TNC tree element in postorder traversal.
+ * @znode: previous znode
+ *
+ * This function implements postorder TNC traversal. The LNC is ignored.
+ * Returns the next element or %NULL if @znode is already the last one.
+ */
+struct ubifs_znode *ubifs_tnc_postorder_next(struct ubifs_znode *znode)
+{
+ struct ubifs_znode *zn;
+
+ ubifs_assert(znode);
+ if (unlikely(!znode->parent))
+ return NULL;
+
+ /* Switch to the next index in the parent */
+ zn = ubifs_tnc_find_child(znode->parent, znode->iip + 1);
+ if (!zn)
+ /* This is in fact the last child, return parent */
+ return znode->parent;
+
+ /* Go to the first znode in this new subtree */
+ return ubifs_tnc_postorder_first(zn);
+}
+
+/**
+ * ubifs_destroy_tnc_subtree - destroy all znodes connected to a subtree.
+ * @znode: znode defining subtree to destroy
+ *
+ * This function destroys subtree of the TNC tree. Returns number of clean
+ * znodes in the subtree.
+ */
+long ubifs_destroy_tnc_subtree(struct ubifs_znode *znode)
+{
+ struct ubifs_znode *zn = ubifs_tnc_postorder_first(znode);
+ long clean_freed = 0;
+ int n;
+
+ ubifs_assert(zn);
+ while (1) {
+ for (n = 0; n < zn->child_cnt; n++) {
+ if (!zn->zbranch[n].znode)
+ continue;
+
+ if (zn->level > 0 &&
+ !ubifs_zn_dirty(zn->zbranch[n].znode))
+ clean_freed += 1;
+
+ cond_resched();
+ kfree(zn->zbranch[n].znode);
+ }
+
+ if (zn == znode) {
+ if (!ubifs_zn_dirty(zn))
+ clean_freed += 1;
+ kfree(zn);
+ return clean_freed;
+ }
+
+ zn = ubifs_tnc_postorder_next(zn);
+ }
+}
+
+/**
+ * read_znode - read an indexing node from flash and fill znode.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB of the indexing node to read
+ * @offs: node offset
+ * @len: node length
+ * @znode: znode to read to
+ *
+ * This function reads an indexing node from the flash media and fills znode
+ * with the read data. Returns zero in case of success and a negative error
+ * code in case of failure. The read indexing node is validated and if anything
+ * is wrong with it, this function prints complaint messages and returns
+ * %-EINVAL.
+ */
+static int read_znode(struct ubifs_info *c, int lnum, int offs, int len,
+ struct ubifs_znode *znode)
+{
+ int i, err, type, cmp;
+ struct ubifs_idx_node *idx;
+
+ idx = kmalloc(c->max_idx_node_sz, GFP_NOFS);
+ if (!idx)
+ return -ENOMEM;
+
+ err = ubifs_read_node(c, idx, UBIFS_IDX_NODE, len, lnum, offs);
+ if (err < 0) {
+ kfree(idx);
+ return err;
+ }
+
+ znode->child_cnt = le16_to_cpu(idx->child_cnt);
+ znode->level = le16_to_cpu(idx->level);
+
+ dbg_tnc("LEB %d:%d, level %d, %d branch",
+ lnum, offs, znode->level, znode->child_cnt);
+
+ if (znode->child_cnt > c->fanout || znode->level > UBIFS_MAX_LEVELS) {
+ ubifs_err(c, "current fanout %d, branch count %d",
+ c->fanout, znode->child_cnt);
+ ubifs_err(c, "max levels %d, znode level %d",
+ UBIFS_MAX_LEVELS, znode->level);
+ err = 1;
+ goto out_dump;
+ }
+
+ for (i = 0; i < znode->child_cnt; i++) {
+ const struct ubifs_branch *br = ubifs_idx_branch(c, idx, i);
+ struct ubifs_zbranch *zbr = &znode->zbranch[i];
+
+ key_read(c, &br->key, &zbr->key);
+ zbr->lnum = le32_to_cpu(br->lnum);
+ zbr->offs = le32_to_cpu(br->offs);
+ zbr->len = le32_to_cpu(br->len);
+ zbr->znode = NULL;
+
+ /* Validate branch */
+
+ if (zbr->lnum < c->main_first ||
+ zbr->lnum >= c->leb_cnt || zbr->offs < 0 ||
+ zbr->offs + zbr->len > c->leb_size || zbr->offs & 7) {
+ ubifs_err(c, "bad branch %d", i);
+ err = 2;
+ goto out_dump;
+ }
+
+ switch (key_type(c, &zbr->key)) {
+ case UBIFS_INO_KEY:
+ case UBIFS_DATA_KEY:
+ case UBIFS_DENT_KEY:
+ case UBIFS_XENT_KEY:
+ break;
+ default:
+ ubifs_err(c, "bad key type at slot %d: %d",
+ i, key_type(c, &zbr->key));
+ err = 3;
+ goto out_dump;
+ }
+
+ if (znode->level)
+ continue;
+
+ type = key_type(c, &zbr->key);
+ if (c->ranges[type].max_len == 0) {
+ if (zbr->len != c->ranges[type].len) {
+ ubifs_err(c, "bad target node (type %d) length (%d)",
+ type, zbr->len);
+ ubifs_err(c, "have to be %d", c->ranges[type].len);
+ err = 4;
+ goto out_dump;
+ }
+ } else if (zbr->len < c->ranges[type].min_len ||
+ zbr->len > c->ranges[type].max_len) {
+ ubifs_err(c, "bad target node (type %d) length (%d)",
+ type, zbr->len);
+ ubifs_err(c, "have to be in range of %d-%d",
+ c->ranges[type].min_len,
+ c->ranges[type].max_len);
+ err = 5;
+ goto out_dump;
+ }
+ }
+
+ /*
+ * Ensure that the next key is greater or equivalent to the
+ * previous one.
+ */
+ for (i = 0; i < znode->child_cnt - 1; i++) {
+ const union ubifs_key *key1, *key2;
+
+ key1 = &znode->zbranch[i].key;
+ key2 = &znode->zbranch[i + 1].key;
+
+ cmp = keys_cmp(c, key1, key2);
+ if (cmp > 0) {
+ ubifs_err(c, "bad key order (keys %d and %d)", i, i + 1);
+ err = 6;
+ goto out_dump;
+ } else if (cmp == 0 && !is_hash_key(c, key1)) {
+ /* These can only be keys with colliding hash */
+ ubifs_err(c, "keys %d and %d are not hashed but equivalent",
+ i, i + 1);
+ err = 7;
+ goto out_dump;
+ }
+ }
+
+ kfree(idx);
+ return 0;
+
+out_dump:
+ ubifs_err(c, "bad indexing node at LEB %d:%d, error %d", lnum, offs, err);
+ ubifs_dump_node(c, idx);
+ kfree(idx);
+ return -EINVAL;
+}
+
+/**
+ * ubifs_load_znode - load znode to TNC cache.
+ * @c: UBIFS file-system description object
+ * @zbr: znode branch
+ * @parent: znode's parent
+ * @iip: index in parent
+ *
+ * This function loads znode pointed to by @zbr into the TNC cache and
+ * returns pointer to it in case of success and a negative error code in case
+ * of failure.
+ */
+struct ubifs_znode *ubifs_load_znode(struct ubifs_info *c,
+ struct ubifs_zbranch *zbr,
+ struct ubifs_znode *parent, int iip)
+{
+ int err;
+ struct ubifs_znode *znode;
+
+ ubifs_assert(!zbr->znode);
+ /*
+ * A slab cache is not presently used for znodes because the znode size
+ * depends on the fanout which is stored in the superblock.
+ */
+ znode = kzalloc(c->max_znode_sz, GFP_NOFS);
+ if (!znode)
+ return ERR_PTR(-ENOMEM);
+
+ err = read_znode(c, zbr->lnum, zbr->offs, zbr->len, znode);
+ if (err)
+ goto out;
+
+ atomic_long_inc(&c->clean_zn_cnt);
+
+ /*
+ * Increment the global clean znode counter as well. It is OK that
+ * global and per-FS clean znode counters may be inconsistent for some
+ * short time (because we might be preempted at this point), the global
+ * one is only used in shrinker.
+ */
+ atomic_long_inc(&ubifs_clean_zn_cnt);
+
+ zbr->znode = znode;
+ znode->parent = parent;
+ znode->time = get_seconds();
+ znode->iip = iip;
+
+ return znode;
+
+out:
+ kfree(znode);
+ return ERR_PTR(err);
+}
+
+/**
+ * ubifs_tnc_read_node - read a leaf node from the flash media.
+ * @c: UBIFS file-system description object
+ * @zbr: key and position of the node
+ * @node: node is returned here
+ *
+ * This function reads a node defined by @zbr from the flash media. Returns
+ * zero in case of success or a negative negative error code in case of
+ * failure.
+ */
+int ubifs_tnc_read_node(struct ubifs_info *c, struct ubifs_zbranch *zbr,
+ void *node)
+{
+ union ubifs_key key1, *key = &zbr->key;
+ int err, type = key_type(c, key);
+ struct ubifs_wbuf *wbuf;
+
+ /*
+ * 'zbr' has to point to on-flash node. The node may sit in a bud and
+ * may even be in a write buffer, so we have to take care about this.
+ */
+ wbuf = ubifs_get_wbuf(c, zbr->lnum);
+ if (wbuf)
+ err = ubifs_read_node_wbuf(wbuf, node, type, zbr->len,
+ zbr->lnum, zbr->offs);
+ else
+ err = ubifs_read_node(c, node, type, zbr->len, zbr->lnum,
+ zbr->offs);
+
+ if (err) {
+ dbg_tnck(key, "key ");
+ return err;
+ }
+
+ /* Make sure the key of the read node is correct */
+ key_read(c, node + UBIFS_KEY_OFFSET, &key1);
+ if (!keys_eq(c, key, &key1)) {
+ ubifs_err(c, "bad key in node at LEB %d:%d",
+ zbr->lnum, zbr->offs);
+ dbg_tnck(key, "looked for key ");
+ dbg_tnck(&key1, "but found node's key ");
+ ubifs_dump_node(c, node);
+ return -EINVAL;
+ }
+
+ return 0;
+}
diff --git a/roms/u-boot/fs/ubifs/ubifs-media.h b/roms/u-boot/fs/ubifs/ubifs-media.h
new file mode 100644
index 000000000..2b5b26a01
--- /dev/null
+++ b/roms/u-boot/fs/ubifs/ubifs-media.h
@@ -0,0 +1,784 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ *
+ * Authors: Artem Bityutskiy (Битюцкий Артём)
+ * Adrian Hunter
+ */
+
+/*
+ * This file describes UBIFS on-flash format and contains definitions of all the
+ * relevant data structures and constants.
+ *
+ * All UBIFS on-flash objects are stored in the form of nodes. All nodes start
+ * with the UBIFS node magic number and have the same common header. Nodes
+ * always sit at 8-byte aligned positions on the media and node header sizes are
+ * also 8-byte aligned (except for the indexing node and the padding node).
+ */
+
+#ifndef __UBIFS_MEDIA_H__
+#define __UBIFS_MEDIA_H__
+
+/* UBIFS node magic number (must not have the padding byte first or last) */
+#define UBIFS_NODE_MAGIC 0x06101831
+
+/*
+ * UBIFS on-flash format version. This version is increased when the on-flash
+ * format is changing. If this happens, UBIFS is will support older versions as
+ * well. But older UBIFS code will not support newer formats. Format changes
+ * will be rare and only when absolutely necessary, e.g. to fix a bug or to add
+ * a new feature.
+ *
+ * UBIFS went into mainline kernel with format version 4. The older formats
+ * were development formats.
+ */
+#define UBIFS_FORMAT_VERSION 4
+
+/*
+ * Read-only compatibility version. If the UBIFS format is changed, older UBIFS
+ * implementations will not be able to mount newer formats in read-write mode.
+ * However, depending on the change, it may be possible to mount newer formats
+ * in R/O mode. This is indicated by the R/O compatibility version which is
+ * stored in the super-block.
+ *
+ * This is needed to support boot-loaders which only need R/O mounting. With
+ * this flag it is possible to do UBIFS format changes without a need to update
+ * boot-loaders.
+ */
+#define UBIFS_RO_COMPAT_VERSION 0
+
+/* Minimum logical eraseblock size in bytes */
+#define UBIFS_MIN_LEB_SZ (15*1024)
+
+/* Initial CRC32 value used when calculating CRC checksums */
+#define UBIFS_CRC32_INIT 0xFFFFFFFFU
+
+/*
+ * UBIFS does not try to compress data if its length is less than the below
+ * constant.
+ */
+#define UBIFS_MIN_COMPR_LEN 128
+
+/*
+ * If compressed data length is less than %UBIFS_MIN_COMPRESS_DIFF bytes
+ * shorter than uncompressed data length, UBIFS prefers to leave this data
+ * node uncompress, because it'll be read faster.
+ */
+#define UBIFS_MIN_COMPRESS_DIFF 64
+
+/* Root inode number */
+#define UBIFS_ROOT_INO 1
+
+/* Lowest inode number used for regular inodes (not UBIFS-only internal ones) */
+#define UBIFS_FIRST_INO 64
+
+/*
+ * Maximum file name and extended attribute length (must be a multiple of 8,
+ * minus 1).
+ */
+#define UBIFS_MAX_NLEN 255
+
+/* Maximum number of data journal heads */
+#define UBIFS_MAX_JHEADS 1
+
+/*
+ * Size of UBIFS data block. Note, UBIFS is not a block oriented file-system,
+ * which means that it does not treat the underlying media as consisting of
+ * blocks like in case of hard drives. Do not be confused. UBIFS block is just
+ * the maximum amount of data which one data node can have or which can be
+ * attached to an inode node.
+ */
+#define UBIFS_BLOCK_SIZE 4096
+#define UBIFS_BLOCK_SHIFT 12
+
+/* UBIFS padding byte pattern (must not be first or last byte of node magic) */
+#define UBIFS_PADDING_BYTE 0xCE
+
+/* Maximum possible key length */
+#define UBIFS_MAX_KEY_LEN 16
+
+/* Key length ("simple" format) */
+#define UBIFS_SK_LEN 8
+
+/* Minimum index tree fanout */
+#define UBIFS_MIN_FANOUT 3
+
+/* Maximum number of levels in UBIFS indexing B-tree */
+#define UBIFS_MAX_LEVELS 512
+
+/* Maximum amount of data attached to an inode in bytes */
+#define UBIFS_MAX_INO_DATA UBIFS_BLOCK_SIZE
+
+/* LEB Properties Tree fanout (must be power of 2) and fanout shift */
+#define UBIFS_LPT_FANOUT 4
+#define UBIFS_LPT_FANOUT_SHIFT 2
+
+/* LEB Properties Tree bit field sizes */
+#define UBIFS_LPT_CRC_BITS 16
+#define UBIFS_LPT_CRC_BYTES 2
+#define UBIFS_LPT_TYPE_BITS 4
+
+/* The key is always at the same position in all keyed nodes */
+#define UBIFS_KEY_OFFSET offsetof(struct ubifs_ino_node, key)
+
+/* Garbage collector journal head number */
+#define UBIFS_GC_HEAD 0
+/* Base journal head number */
+#define UBIFS_BASE_HEAD 1
+/* Data journal head number */
+#define UBIFS_DATA_HEAD 2
+
+/*
+ * LEB Properties Tree node types.
+ *
+ * UBIFS_LPT_PNODE: LPT leaf node (contains LEB properties)
+ * UBIFS_LPT_NNODE: LPT internal node
+ * UBIFS_LPT_LTAB: LPT's own lprops table
+ * UBIFS_LPT_LSAVE: LPT's save table (big model only)
+ * UBIFS_LPT_NODE_CNT: count of LPT node types
+ * UBIFS_LPT_NOT_A_NODE: all ones (15 for 4 bits) is never a valid node type
+ */
+enum {
+ UBIFS_LPT_PNODE,
+ UBIFS_LPT_NNODE,
+ UBIFS_LPT_LTAB,
+ UBIFS_LPT_LSAVE,
+ UBIFS_LPT_NODE_CNT,
+ UBIFS_LPT_NOT_A_NODE = (1 << UBIFS_LPT_TYPE_BITS) - 1,
+};
+
+/*
+ * UBIFS inode types.
+ *
+ * UBIFS_ITYPE_REG: regular file
+ * UBIFS_ITYPE_DIR: directory
+ * UBIFS_ITYPE_LNK: soft link
+ * UBIFS_ITYPE_BLK: block device node
+ * UBIFS_ITYPE_CHR: character device node
+ * UBIFS_ITYPE_FIFO: fifo
+ * UBIFS_ITYPE_SOCK: socket
+ * UBIFS_ITYPES_CNT: count of supported file types
+ */
+enum {
+ UBIFS_ITYPE_REG,
+ UBIFS_ITYPE_DIR,
+ UBIFS_ITYPE_LNK,
+ UBIFS_ITYPE_BLK,
+ UBIFS_ITYPE_CHR,
+ UBIFS_ITYPE_FIFO,
+ UBIFS_ITYPE_SOCK,
+ UBIFS_ITYPES_CNT,
+};
+
+/*
+ * Supported key hash functions.
+ *
+ * UBIFS_KEY_HASH_R5: R5 hash
+ * UBIFS_KEY_HASH_TEST: test hash which just returns first 4 bytes of the name
+ */
+enum {
+ UBIFS_KEY_HASH_R5,
+ UBIFS_KEY_HASH_TEST,
+};
+
+/*
+ * Supported key formats.
+ *
+ * UBIFS_SIMPLE_KEY_FMT: simple key format
+ */
+enum {
+ UBIFS_SIMPLE_KEY_FMT,
+};
+
+/*
+ * The simple key format uses 29 bits for storing UBIFS block number and hash
+ * value.
+ */
+#define UBIFS_S_KEY_BLOCK_BITS 29
+#define UBIFS_S_KEY_BLOCK_MASK 0x1FFFFFFF
+#define UBIFS_S_KEY_HASH_BITS UBIFS_S_KEY_BLOCK_BITS
+#define UBIFS_S_KEY_HASH_MASK UBIFS_S_KEY_BLOCK_MASK
+
+/*
+ * Key types.
+ *
+ * UBIFS_INO_KEY: inode node key
+ * UBIFS_DATA_KEY: data node key
+ * UBIFS_DENT_KEY: directory entry node key
+ * UBIFS_XENT_KEY: extended attribute entry key
+ * UBIFS_KEY_TYPES_CNT: number of supported key types
+ */
+enum {
+ UBIFS_INO_KEY,
+ UBIFS_DATA_KEY,
+ UBIFS_DENT_KEY,
+ UBIFS_XENT_KEY,
+ UBIFS_KEY_TYPES_CNT,
+};
+
+/* Count of LEBs reserved for the superblock area */
+#define UBIFS_SB_LEBS 1
+/* Count of LEBs reserved for the master area */
+#define UBIFS_MST_LEBS 2
+
+/* First LEB of the superblock area */
+#define UBIFS_SB_LNUM 0
+/* First LEB of the master area */
+#define UBIFS_MST_LNUM (UBIFS_SB_LNUM + UBIFS_SB_LEBS)
+/* First LEB of the log area */
+#define UBIFS_LOG_LNUM (UBIFS_MST_LNUM + UBIFS_MST_LEBS)
+
+/*
+ * The below constants define the absolute minimum values for various UBIFS
+ * media areas. Many of them actually depend of flash geometry and the FS
+ * configuration (number of journal heads, orphan LEBs, etc). This means that
+ * the smallest volume size which can be used for UBIFS cannot be pre-defined
+ * by these constants. The file-system that meets the below limitation will not
+ * necessarily mount. UBIFS does run-time calculations and validates the FS
+ * size.
+ */
+
+/* Minimum number of logical eraseblocks in the log */
+#define UBIFS_MIN_LOG_LEBS 2
+/* Minimum number of bud logical eraseblocks (one for each head) */
+#define UBIFS_MIN_BUD_LEBS 3
+/* Minimum number of journal logical eraseblocks */
+#define UBIFS_MIN_JNL_LEBS (UBIFS_MIN_LOG_LEBS + UBIFS_MIN_BUD_LEBS)
+/* Minimum number of LPT area logical eraseblocks */
+#define UBIFS_MIN_LPT_LEBS 2
+/* Minimum number of orphan area logical eraseblocks */
+#define UBIFS_MIN_ORPH_LEBS 1
+/*
+ * Minimum number of main area logical eraseblocks (buds, 3 for the index, 1
+ * for GC, 1 for deletions, and at least 1 for committed data).
+ */
+#define UBIFS_MIN_MAIN_LEBS (UBIFS_MIN_BUD_LEBS + 6)
+
+/* Minimum number of logical eraseblocks */
+#define UBIFS_MIN_LEB_CNT (UBIFS_SB_LEBS + UBIFS_MST_LEBS + \
+ UBIFS_MIN_LOG_LEBS + UBIFS_MIN_LPT_LEBS + \
+ UBIFS_MIN_ORPH_LEBS + UBIFS_MIN_MAIN_LEBS)
+
+/* Node sizes (N.B. these are guaranteed to be multiples of 8) */
+#define UBIFS_CH_SZ sizeof(struct ubifs_ch)
+#define UBIFS_INO_NODE_SZ sizeof(struct ubifs_ino_node)
+#define UBIFS_DATA_NODE_SZ sizeof(struct ubifs_data_node)
+#define UBIFS_DENT_NODE_SZ sizeof(struct ubifs_dent_node)
+#define UBIFS_TRUN_NODE_SZ sizeof(struct ubifs_trun_node)
+#define UBIFS_PAD_NODE_SZ sizeof(struct ubifs_pad_node)
+#define UBIFS_SB_NODE_SZ sizeof(struct ubifs_sb_node)
+#define UBIFS_MST_NODE_SZ sizeof(struct ubifs_mst_node)
+#define UBIFS_REF_NODE_SZ sizeof(struct ubifs_ref_node)
+#define UBIFS_IDX_NODE_SZ sizeof(struct ubifs_idx_node)
+#define UBIFS_CS_NODE_SZ sizeof(struct ubifs_cs_node)
+#define UBIFS_ORPH_NODE_SZ sizeof(struct ubifs_orph_node)
+/* Extended attribute entry nodes are identical to directory entry nodes */
+#define UBIFS_XENT_NODE_SZ UBIFS_DENT_NODE_SZ
+/* Only this does not have to be multiple of 8 bytes */
+#define UBIFS_BRANCH_SZ sizeof(struct ubifs_branch)
+
+/* Maximum node sizes (N.B. these are guaranteed to be multiples of 8) */
+#define UBIFS_MAX_DATA_NODE_SZ (UBIFS_DATA_NODE_SZ + UBIFS_BLOCK_SIZE)
+#define UBIFS_MAX_INO_NODE_SZ (UBIFS_INO_NODE_SZ + UBIFS_MAX_INO_DATA)
+#define UBIFS_MAX_DENT_NODE_SZ (UBIFS_DENT_NODE_SZ + UBIFS_MAX_NLEN + 1)
+#define UBIFS_MAX_XENT_NODE_SZ UBIFS_MAX_DENT_NODE_SZ
+
+/* The largest UBIFS node */
+#define UBIFS_MAX_NODE_SZ UBIFS_MAX_INO_NODE_SZ
+
+/*
+ * On-flash inode flags.
+ *
+ * UBIFS_COMPR_FL: use compression for this inode
+ * UBIFS_SYNC_FL: I/O on this inode has to be synchronous
+ * UBIFS_IMMUTABLE_FL: inode is immutable
+ * UBIFS_APPEND_FL: writes to the inode may only append data
+ * UBIFS_DIRSYNC_FL: I/O on this directory inode has to be synchronous
+ * UBIFS_XATTR_FL: this inode is the inode for an extended attribute value
+ *
+ * Note, these are on-flash flags which correspond to ioctl flags
+ * (@FS_COMPR_FL, etc). They have the same values now, but generally, do not
+ * have to be the same.
+ */
+enum {
+ UBIFS_COMPR_FL = 0x01,
+ UBIFS_SYNC_FL = 0x02,
+ UBIFS_IMMUTABLE_FL = 0x04,
+ UBIFS_APPEND_FL = 0x08,
+ UBIFS_DIRSYNC_FL = 0x10,
+ UBIFS_XATTR_FL = 0x20,
+};
+
+/* Inode flag bits used by UBIFS */
+#define UBIFS_FL_MASK 0x0000001F
+
+/*
+ * UBIFS compression algorithms.
+ *
+ * UBIFS_COMPR_NONE: no compression
+ * UBIFS_COMPR_LZO: LZO compression
+ * UBIFS_COMPR_ZLIB: ZLIB compression
+ * UBIFS_COMPR_TYPES_CNT: count of supported compression types
+ */
+enum {
+ UBIFS_COMPR_NONE,
+ UBIFS_COMPR_LZO,
+ UBIFS_COMPR_ZLIB,
+ UBIFS_COMPR_TYPES_CNT,
+};
+
+/*
+ * UBIFS node types.
+ *
+ * UBIFS_INO_NODE: inode node
+ * UBIFS_DATA_NODE: data node
+ * UBIFS_DENT_NODE: directory entry node
+ * UBIFS_XENT_NODE: extended attribute node
+ * UBIFS_TRUN_NODE: truncation node
+ * UBIFS_PAD_NODE: padding node
+ * UBIFS_SB_NODE: superblock node
+ * UBIFS_MST_NODE: master node
+ * UBIFS_REF_NODE: LEB reference node
+ * UBIFS_IDX_NODE: index node
+ * UBIFS_CS_NODE: commit start node
+ * UBIFS_ORPH_NODE: orphan node
+ * UBIFS_NODE_TYPES_CNT: count of supported node types
+ *
+ * Note, we index arrays by these numbers, so keep them low and contiguous.
+ * Node type constants for inodes, direntries and so on have to be the same as
+ * corresponding key type constants.
+ */
+enum {
+ UBIFS_INO_NODE,
+ UBIFS_DATA_NODE,
+ UBIFS_DENT_NODE,
+ UBIFS_XENT_NODE,
+ UBIFS_TRUN_NODE,
+ UBIFS_PAD_NODE,
+ UBIFS_SB_NODE,
+ UBIFS_MST_NODE,
+ UBIFS_REF_NODE,
+ UBIFS_IDX_NODE,
+ UBIFS_CS_NODE,
+ UBIFS_ORPH_NODE,
+ UBIFS_NODE_TYPES_CNT,
+};
+
+/*
+ * Master node flags.
+ *
+ * UBIFS_MST_DIRTY: rebooted uncleanly - master node is dirty
+ * UBIFS_MST_NO_ORPHS: no orphan inodes present
+ * UBIFS_MST_RCVRY: written by recovery
+ */
+enum {
+ UBIFS_MST_DIRTY = 1,
+ UBIFS_MST_NO_ORPHS = 2,
+ UBIFS_MST_RCVRY = 4,
+};
+
+/*
+ * Node group type (used by recovery to recover whole group or none).
+ *
+ * UBIFS_NO_NODE_GROUP: this node is not part of a group
+ * UBIFS_IN_NODE_GROUP: this node is a part of a group
+ * UBIFS_LAST_OF_NODE_GROUP: this node is the last in a group
+ */
+enum {
+ UBIFS_NO_NODE_GROUP = 0,
+ UBIFS_IN_NODE_GROUP,
+ UBIFS_LAST_OF_NODE_GROUP,
+};
+
+/*
+ * Superblock flags.
+ *
+ * UBIFS_FLG_BIGLPT: if "big" LPT model is used if set
+ * UBIFS_FLG_SPACE_FIXUP: first-mount "fixup" of free space within LEBs needed
+ */
+enum {
+ UBIFS_FLG_BIGLPT = 0x02,
+ UBIFS_FLG_SPACE_FIXUP = 0x04,
+};
+
+/**
+ * struct ubifs_ch - common header node.
+ * @magic: UBIFS node magic number (%UBIFS_NODE_MAGIC)
+ * @crc: CRC-32 checksum of the node header
+ * @sqnum: sequence number
+ * @len: full node length
+ * @node_type: node type
+ * @group_type: node group type
+ * @padding: reserved for future, zeroes
+ *
+ * Every UBIFS node starts with this common part. If the node has a key, the
+ * key always goes next.
+ */
+struct ubifs_ch {
+ __le32 magic;
+ __le32 crc;
+ __le64 sqnum;
+ __le32 len;
+ __u8 node_type;
+ __u8 group_type;
+ __u8 padding[2];
+} __packed;
+
+/**
+ * union ubifs_dev_desc - device node descriptor.
+ * @new: new type device descriptor
+ * @huge: huge type device descriptor
+ *
+ * This data structure describes major/minor numbers of a device node. In an
+ * inode is a device node then its data contains an object of this type. UBIFS
+ * uses standard Linux "new" and "huge" device node encodings.
+ */
+union ubifs_dev_desc {
+ __le32 new;
+ __le64 huge;
+} __packed;
+
+/**
+ * struct ubifs_ino_node - inode node.
+ * @ch: common header
+ * @key: node key
+ * @creat_sqnum: sequence number at time of creation
+ * @size: inode size in bytes (amount of uncompressed data)
+ * @atime_sec: access time seconds
+ * @ctime_sec: creation time seconds
+ * @mtime_sec: modification time seconds
+ * @atime_nsec: access time nanoseconds
+ * @ctime_nsec: creation time nanoseconds
+ * @mtime_nsec: modification time nanoseconds
+ * @nlink: number of hard links
+ * @uid: owner ID
+ * @gid: group ID
+ * @mode: access flags
+ * @flags: per-inode flags (%UBIFS_COMPR_FL, %UBIFS_SYNC_FL, etc)
+ * @data_len: inode data length
+ * @xattr_cnt: count of extended attributes this inode has
+ * @xattr_size: summarized size of all extended attributes in bytes
+ * @padding1: reserved for future, zeroes
+ * @xattr_names: sum of lengths of all extended attribute names belonging to
+ * this inode
+ * @compr_type: compression type used for this inode
+ * @padding2: reserved for future, zeroes
+ * @data: data attached to the inode
+ *
+ * Note, even though inode compression type is defined by @compr_type, some
+ * nodes of this inode may be compressed with different compressor - this
+ * happens if compression type is changed while the inode already has data
+ * nodes. But @compr_type will be use for further writes to the inode.
+ *
+ * Note, do not forget to amend 'zero_ino_node_unused()' function when changing
+ * the padding fields.
+ */
+struct ubifs_ino_node {
+ struct ubifs_ch ch;
+ __u8 key[UBIFS_MAX_KEY_LEN];
+ __le64 creat_sqnum;
+ __le64 size;
+ __le64 atime_sec;
+ __le64 ctime_sec;
+ __le64 mtime_sec;
+ __le32 atime_nsec;
+ __le32 ctime_nsec;
+ __le32 mtime_nsec;
+ __le32 nlink;
+ __le32 uid;
+ __le32 gid;
+ __le32 mode;
+ __le32 flags;
+ __le32 data_len;
+ __le32 xattr_cnt;
+ __le32 xattr_size;
+ __u8 padding1[4]; /* Watch 'zero_ino_node_unused()' if changing! */
+ __le32 xattr_names;
+ __le16 compr_type;
+ __u8 padding2[26]; /* Watch 'zero_ino_node_unused()' if changing! */
+ __u8 data[];
+} __packed;
+
+/**
+ * struct ubifs_dent_node - directory entry node.
+ * @ch: common header
+ * @key: node key
+ * @inum: target inode number
+ * @padding1: reserved for future, zeroes
+ * @type: type of the target inode (%UBIFS_ITYPE_REG, %UBIFS_ITYPE_DIR, etc)
+ * @nlen: name length
+ * @padding2: reserved for future, zeroes
+ * @name: zero-terminated name
+ *
+ * Note, do not forget to amend 'zero_dent_node_unused()' function when
+ * changing the padding fields.
+ */
+struct ubifs_dent_node {
+ struct ubifs_ch ch;
+ __u8 key[UBIFS_MAX_KEY_LEN];
+ __le64 inum;
+ __u8 padding1;
+ __u8 type;
+ __le16 nlen;
+ __u8 padding2[4]; /* Watch 'zero_dent_node_unused()' if changing! */
+#ifndef __UBOOT__
+ __u8 name[];
+#else
+ char name[];
+#endif
+} __packed;
+
+/**
+ * struct ubifs_data_node - data node.
+ * @ch: common header
+ * @key: node key
+ * @size: uncompressed data size in bytes
+ * @compr_type: compression type (%UBIFS_COMPR_NONE, %UBIFS_COMPR_LZO, etc)
+ * @padding: reserved for future, zeroes
+ * @data: data
+ *
+ * Note, do not forget to amend 'zero_data_node_unused()' function when
+ * changing the padding fields.
+ */
+struct ubifs_data_node {
+ struct ubifs_ch ch;
+ __u8 key[UBIFS_MAX_KEY_LEN];
+ __le32 size;
+ __le16 compr_type;
+ __u8 padding[2]; /* Watch 'zero_data_node_unused()' if changing! */
+ __u8 data[];
+} __packed;
+
+/**
+ * struct ubifs_trun_node - truncation node.
+ * @ch: common header
+ * @inum: truncated inode number
+ * @padding: reserved for future, zeroes
+ * @old_size: size before truncation
+ * @new_size: size after truncation
+ *
+ * This node exists only in the journal and never goes to the main area. Note,
+ * do not forget to amend 'zero_trun_node_unused()' function when changing the
+ * padding fields.
+ */
+struct ubifs_trun_node {
+ struct ubifs_ch ch;
+ __le32 inum;
+ __u8 padding[12]; /* Watch 'zero_trun_node_unused()' if changing! */
+ __le64 old_size;
+ __le64 new_size;
+} __packed;
+
+/**
+ * struct ubifs_pad_node - padding node.
+ * @ch: common header
+ * @pad_len: how many bytes after this node are unused (because padded)
+ * @padding: reserved for future, zeroes
+ */
+struct ubifs_pad_node {
+ struct ubifs_ch ch;
+ __le32 pad_len;
+} __packed;
+
+/**
+ * struct ubifs_sb_node - superblock node.
+ * @ch: common header
+ * @padding: reserved for future, zeroes
+ * @key_hash: type of hash function used in keys
+ * @key_fmt: format of the key
+ * @flags: file-system flags (%UBIFS_FLG_BIGLPT, etc)
+ * @min_io_size: minimal input/output unit size
+ * @leb_size: logical eraseblock size in bytes
+ * @leb_cnt: count of LEBs used by file-system
+ * @max_leb_cnt: maximum count of LEBs used by file-system
+ * @max_bud_bytes: maximum amount of data stored in buds
+ * @log_lebs: log size in logical eraseblocks
+ * @lpt_lebs: number of LEBs used for lprops table
+ * @orph_lebs: number of LEBs used for recording orphans
+ * @jhead_cnt: count of journal heads
+ * @fanout: tree fanout (max. number of links per indexing node)
+ * @lsave_cnt: number of LEB numbers in LPT's save table
+ * @fmt_version: UBIFS on-flash format version
+ * @default_compr: default compression algorithm (%UBIFS_COMPR_LZO, etc)
+ * @padding1: reserved for future, zeroes
+ * @rp_uid: reserve pool UID
+ * @rp_gid: reserve pool GID
+ * @rp_size: size of the reserved pool in bytes
+ * @padding2: reserved for future, zeroes
+ * @time_gran: time granularity in nanoseconds
+ * @uuid: UUID generated when the file system image was created
+ * @ro_compat_version: UBIFS R/O compatibility version
+ */
+struct ubifs_sb_node {
+ struct ubifs_ch ch;
+ __u8 padding[2];
+ __u8 key_hash;
+ __u8 key_fmt;
+ __le32 flags;
+ __le32 min_io_size;
+ __le32 leb_size;
+ __le32 leb_cnt;
+ __le32 max_leb_cnt;
+ __le64 max_bud_bytes;
+ __le32 log_lebs;
+ __le32 lpt_lebs;
+ __le32 orph_lebs;
+ __le32 jhead_cnt;
+ __le32 fanout;
+ __le32 lsave_cnt;
+ __le32 fmt_version;
+ __le16 default_compr;
+ __u8 padding1[2];
+ __le32 rp_uid;
+ __le32 rp_gid;
+ __le64 rp_size;
+ __le32 time_gran;
+ __u8 uuid[16];
+ __le32 ro_compat_version;
+ __u8 padding2[3968];
+} __packed;
+
+/**
+ * struct ubifs_mst_node - master node.
+ * @ch: common header
+ * @highest_inum: highest inode number in the committed index
+ * @cmt_no: commit number
+ * @flags: various flags (%UBIFS_MST_DIRTY, etc)
+ * @log_lnum: start of the log
+ * @root_lnum: LEB number of the root indexing node
+ * @root_offs: offset within @root_lnum
+ * @root_len: root indexing node length
+ * @gc_lnum: LEB reserved for garbage collection (%-1 value means the LEB was
+ * not reserved and should be reserved on mount)
+ * @ihead_lnum: LEB number of index head
+ * @ihead_offs: offset of index head
+ * @index_size: size of index on flash
+ * @total_free: total free space in bytes
+ * @total_dirty: total dirty space in bytes
+ * @total_used: total used space in bytes (includes only data LEBs)
+ * @total_dead: total dead space in bytes (includes only data LEBs)
+ * @total_dark: total dark space in bytes (includes only data LEBs)
+ * @lpt_lnum: LEB number of LPT root nnode
+ * @lpt_offs: offset of LPT root nnode
+ * @nhead_lnum: LEB number of LPT head
+ * @nhead_offs: offset of LPT head
+ * @ltab_lnum: LEB number of LPT's own lprops table
+ * @ltab_offs: offset of LPT's own lprops table
+ * @lsave_lnum: LEB number of LPT's save table (big model only)
+ * @lsave_offs: offset of LPT's save table (big model only)
+ * @lscan_lnum: LEB number of last LPT scan
+ * @empty_lebs: number of empty logical eraseblocks
+ * @idx_lebs: number of indexing logical eraseblocks
+ * @leb_cnt: count of LEBs used by file-system
+ * @padding: reserved for future, zeroes
+ */
+struct ubifs_mst_node {
+ struct ubifs_ch ch;
+ __le64 highest_inum;
+ __le64 cmt_no;
+ __le32 flags;
+ __le32 log_lnum;
+ __le32 root_lnum;
+ __le32 root_offs;
+ __le32 root_len;
+ __le32 gc_lnum;
+ __le32 ihead_lnum;
+ __le32 ihead_offs;
+ __le64 index_size;
+ __le64 total_free;
+ __le64 total_dirty;
+ __le64 total_used;
+ __le64 total_dead;
+ __le64 total_dark;
+ __le32 lpt_lnum;
+ __le32 lpt_offs;
+ __le32 nhead_lnum;
+ __le32 nhead_offs;
+ __le32 ltab_lnum;
+ __le32 ltab_offs;
+ __le32 lsave_lnum;
+ __le32 lsave_offs;
+ __le32 lscan_lnum;
+ __le32 empty_lebs;
+ __le32 idx_lebs;
+ __le32 leb_cnt;
+ __u8 padding[344];
+} __packed;
+
+/**
+ * struct ubifs_ref_node - logical eraseblock reference node.
+ * @ch: common header
+ * @lnum: the referred logical eraseblock number
+ * @offs: start offset in the referred LEB
+ * @jhead: journal head number
+ * @padding: reserved for future, zeroes
+ */
+struct ubifs_ref_node {
+ struct ubifs_ch ch;
+ __le32 lnum;
+ __le32 offs;
+ __le32 jhead;
+ __u8 padding[28];
+} __packed;
+
+/**
+ * struct ubifs_branch - key/reference/length branch
+ * @lnum: LEB number of the target node
+ * @offs: offset within @lnum
+ * @len: target node length
+ * @key: key
+ */
+struct ubifs_branch {
+ __le32 lnum;
+ __le32 offs;
+ __le32 len;
+#ifndef __UBOOT__
+ __u8 key[];
+#else
+ char key[];
+#endif
+} __packed;
+
+/**
+ * struct ubifs_idx_node - indexing node.
+ * @ch: common header
+ * @child_cnt: number of child index nodes
+ * @level: tree level
+ * @branches: LEB number / offset / length / key branches
+ */
+struct ubifs_idx_node {
+ struct ubifs_ch ch;
+ __le16 child_cnt;
+ __le16 level;
+#ifndef __UBOOT__
+ __u8 branches[];
+#else
+ char branches[];
+#endif
+} __packed;
+
+/**
+ * struct ubifs_cs_node - commit start node.
+ * @ch: common header
+ * @cmt_no: commit number
+ */
+struct ubifs_cs_node {
+ struct ubifs_ch ch;
+ __le64 cmt_no;
+} __packed;
+
+/**
+ * struct ubifs_orph_node - orphan node.
+ * @ch: common header
+ * @cmt_no: commit number (also top bit is set on the last node of the commit)
+ * @inos: inode numbers of orphans
+ */
+struct ubifs_orph_node {
+ struct ubifs_ch ch;
+ __le64 cmt_no;
+ __le64 inos[];
+} __packed;
+
+#endif /* __UBIFS_MEDIA_H__ */
diff --git a/roms/u-boot/fs/ubifs/ubifs.c b/roms/u-boot/fs/ubifs/ubifs.c
new file mode 100644
index 000000000..d6be5c947
--- /dev/null
+++ b/roms/u-boot/fs/ubifs/ubifs.c
@@ -0,0 +1,950 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ *
+ * (C) Copyright 2008-2010
+ * Stefan Roese, DENX Software Engineering, sr@denx.de.
+ *
+ * Authors: Artem Bityutskiy (Битюцкий Артём)
+ * Adrian Hunter
+ */
+
+#include <common.h>
+#include <env.h>
+#include <gzip.h>
+#include <log.h>
+#include <malloc.h>
+#include <memalign.h>
+#include <asm/global_data.h>
+#include "ubifs.h"
+#include <part.h>
+#include <dm/devres.h>
+#include <u-boot/zlib.h>
+
+#include <linux/compat.h>
+#include <linux/err.h>
+#include <linux/lzo.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* compress.c */
+
+/*
+ * We need a wrapper for zunzip() because the parameters are
+ * incompatible with the lzo decompressor.
+ */
+static int gzip_decompress(const unsigned char *in, size_t in_len,
+ unsigned char *out, size_t *out_len)
+{
+ return zunzip(out, *out_len, (unsigned char *)in,
+ (unsigned long *)out_len, 0, 0);
+}
+
+/* Fake description object for the "none" compressor */
+static struct ubifs_compressor none_compr = {
+ .compr_type = UBIFS_COMPR_NONE,
+ .name = "none",
+ .capi_name = "",
+ .decompress = NULL,
+};
+
+static struct ubifs_compressor lzo_compr = {
+ .compr_type = UBIFS_COMPR_LZO,
+#ifndef __UBOOT__
+ .comp_mutex = &lzo_mutex,
+#endif
+ .name = "lzo",
+ .capi_name = "lzo",
+ .decompress = lzo1x_decompress_safe,
+};
+
+static struct ubifs_compressor zlib_compr = {
+ .compr_type = UBIFS_COMPR_ZLIB,
+#ifndef __UBOOT__
+ .comp_mutex = &deflate_mutex,
+ .decomp_mutex = &inflate_mutex,
+#endif
+ .name = "zlib",
+ .capi_name = "deflate",
+ .decompress = gzip_decompress,
+};
+
+/* All UBIFS compressors */
+struct ubifs_compressor *ubifs_compressors[UBIFS_COMPR_TYPES_CNT];
+
+
+#ifdef __UBOOT__
+
+struct crypto_comp {
+ int compressor;
+};
+
+static inline struct crypto_comp
+*crypto_alloc_comp(const char *alg_name, u32 type, u32 mask)
+{
+ struct ubifs_compressor *comp;
+ struct crypto_comp *ptr;
+ int i = 0;
+
+ ptr = malloc_cache_aligned(sizeof(struct crypto_comp));
+ while (i < UBIFS_COMPR_TYPES_CNT) {
+ comp = ubifs_compressors[i];
+ if (!comp) {
+ i++;
+ continue;
+ }
+ if (strncmp(alg_name, comp->capi_name, strlen(alg_name)) == 0) {
+ ptr->compressor = i;
+ return ptr;
+ }
+ i++;
+ }
+ if (i >= UBIFS_COMPR_TYPES_CNT) {
+ dbg_gen("invalid compression type %s", alg_name);
+ free (ptr);
+ return NULL;
+ }
+ return ptr;
+}
+static inline int
+crypto_comp_decompress(const struct ubifs_info *c, struct crypto_comp *tfm,
+ const u8 *src, unsigned int slen, u8 *dst,
+ unsigned int *dlen)
+{
+ struct ubifs_compressor *compr = ubifs_compressors[tfm->compressor];
+ int err;
+ size_t tmp_len = *dlen;
+
+ if (compr->compr_type == UBIFS_COMPR_NONE) {
+ memcpy(dst, src, slen);
+ *dlen = slen;
+ return 0;
+ }
+
+ err = compr->decompress(src, slen, dst, &tmp_len);
+ if (err)
+ ubifs_err(c, "cannot decompress %d bytes, compressor %s, "
+ "error %d", slen, compr->name, err);
+
+ *dlen = tmp_len;
+ return err;
+
+ return 0;
+}
+
+/* from shrinker.c */
+
+/* Global clean znode counter (for all mounted UBIFS instances) */
+atomic_long_t ubifs_clean_zn_cnt;
+
+#endif
+
+/**
+ * ubifs_decompress - decompress data.
+ * @in_buf: data to decompress
+ * @in_len: length of the data to decompress
+ * @out_buf: output buffer where decompressed data should
+ * @out_len: output length is returned here
+ * @compr_type: type of compression
+ *
+ * This function decompresses data from buffer @in_buf into buffer @out_buf.
+ * The length of the uncompressed data is returned in @out_len. This functions
+ * returns %0 on success or a negative error code on failure.
+ */
+int ubifs_decompress(const struct ubifs_info *c, const void *in_buf,
+ int in_len, void *out_buf, int *out_len, int compr_type)
+{
+ int err;
+ struct ubifs_compressor *compr;
+
+ if (unlikely(compr_type < 0 || compr_type >= UBIFS_COMPR_TYPES_CNT)) {
+ ubifs_err(c, "invalid compression type %d", compr_type);
+ return -EINVAL;
+ }
+
+ compr = ubifs_compressors[compr_type];
+
+ if (unlikely(!compr->capi_name)) {
+ ubifs_err(c, "%s compression is not compiled in", compr->name);
+ return -EINVAL;
+ }
+
+ if (compr_type == UBIFS_COMPR_NONE) {
+ memcpy(out_buf, in_buf, in_len);
+ *out_len = in_len;
+ return 0;
+ }
+
+ if (compr->decomp_mutex)
+ mutex_lock(compr->decomp_mutex);
+ err = crypto_comp_decompress(c, compr->cc, in_buf, in_len, out_buf,
+ (unsigned int *)out_len);
+ if (compr->decomp_mutex)
+ mutex_unlock(compr->decomp_mutex);
+ if (err)
+ ubifs_err(c, "cannot decompress %d bytes, compressor %s,"
+ " error %d", in_len, compr->name, err);
+
+ return err;
+}
+
+/**
+ * compr_init - initialize a compressor.
+ * @compr: compressor description object
+ *
+ * This function initializes the requested compressor and returns zero in case
+ * of success or a negative error code in case of failure.
+ */
+static int __init compr_init(struct ubifs_compressor *compr)
+{
+ ubifs_compressors[compr->compr_type] = compr;
+
+#ifdef CONFIG_NEEDS_MANUAL_RELOC
+ ubifs_compressors[compr->compr_type]->name += gd->reloc_off;
+ ubifs_compressors[compr->compr_type]->capi_name += gd->reloc_off;
+ ubifs_compressors[compr->compr_type]->decompress += gd->reloc_off;
+#endif
+
+ if (compr->capi_name) {
+ compr->cc = crypto_alloc_comp(compr->capi_name, 0, 0);
+ if (IS_ERR(compr->cc)) {
+ dbg_gen("cannot initialize compressor %s,"
+ " error %ld", compr->name,
+ PTR_ERR(compr->cc));
+ return PTR_ERR(compr->cc);
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * ubifs_compressors_init - initialize UBIFS compressors.
+ *
+ * This function initializes the compressor which were compiled in. Returns
+ * zero in case of success and a negative error code in case of failure.
+ */
+int __init ubifs_compressors_init(void)
+{
+ int err;
+
+ err = compr_init(&lzo_compr);
+ if (err)
+ return err;
+
+ err = compr_init(&zlib_compr);
+ if (err)
+ return err;
+
+ err = compr_init(&none_compr);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+/*
+ * ubifsls...
+ */
+
+static int filldir(struct ubifs_info *c, const char *name, int namlen,
+ u64 ino, unsigned int d_type)
+{
+ struct inode *inode;
+ char filetime[32];
+
+ switch (d_type) {
+ case UBIFS_ITYPE_REG:
+ printf("\t");
+ break;
+ case UBIFS_ITYPE_DIR:
+ printf("<DIR>\t");
+ break;
+ case UBIFS_ITYPE_LNK:
+ printf("<LNK>\t");
+ break;
+ default:
+ printf("other\t");
+ break;
+ }
+
+ inode = ubifs_iget(c->vfs_sb, ino);
+ if (IS_ERR(inode)) {
+ printf("%s: Error in ubifs_iget(), ino=%lld ret=%p!\n",
+ __func__, ino, inode);
+ return -1;
+ }
+ ctime_r((time_t *)&inode->i_mtime, filetime);
+ printf("%9lld %24.24s ", inode->i_size, filetime);
+#ifndef __UBOOT__
+ ubifs_iput(inode);
+#endif
+
+ printf("%s\n", name);
+
+ return 0;
+}
+
+static int ubifs_printdir(struct file *file, void *dirent)
+{
+ int err, over = 0;
+ struct qstr nm;
+ union ubifs_key key;
+ struct ubifs_dent_node *dent;
+ struct inode *dir = file->f_path.dentry->d_inode;
+ struct ubifs_info *c = dir->i_sb->s_fs_info;
+
+ dbg_gen("dir ino %lu, f_pos %#llx", dir->i_ino, file->f_pos);
+
+ if (file->f_pos > UBIFS_S_KEY_HASH_MASK || file->f_pos == 2)
+ /*
+ * The directory was seek'ed to a senseless position or there
+ * are no more entries.
+ */
+ return 0;
+
+ if (file->f_pos == 1) {
+ /* Find the first entry in TNC and save it */
+ lowest_dent_key(c, &key, dir->i_ino);
+ nm.name = NULL;
+ dent = ubifs_tnc_next_ent(c, &key, &nm);
+ if (IS_ERR(dent)) {
+ err = PTR_ERR(dent);
+ goto out;
+ }
+
+ file->f_pos = key_hash_flash(c, &dent->key);
+ file->private_data = dent;
+ }
+
+ dent = file->private_data;
+ if (!dent) {
+ /*
+ * The directory was seek'ed to and is now readdir'ed.
+ * Find the entry corresponding to @file->f_pos or the
+ * closest one.
+ */
+ dent_key_init_hash(c, &key, dir->i_ino, file->f_pos);
+ nm.name = NULL;
+ dent = ubifs_tnc_next_ent(c, &key, &nm);
+ if (IS_ERR(dent)) {
+ err = PTR_ERR(dent);
+ goto out;
+ }
+ file->f_pos = key_hash_flash(c, &dent->key);
+ file->private_data = dent;
+ }
+
+ while (1) {
+ dbg_gen("feed '%s', ino %llu, new f_pos %#x",
+ dent->name, (unsigned long long)le64_to_cpu(dent->inum),
+ key_hash_flash(c, &dent->key));
+#ifndef __UBOOT__
+ ubifs_assert(le64_to_cpu(dent->ch.sqnum) > ubifs_inode(dir)->creat_sqnum);
+#endif
+
+ nm.len = le16_to_cpu(dent->nlen);
+ over = filldir(c, (char *)dent->name, nm.len,
+ le64_to_cpu(dent->inum), dent->type);
+ if (over)
+ return 0;
+
+ /* Switch to the next entry */
+ key_read(c, &dent->key, &key);
+ nm.name = (char *)dent->name;
+ dent = ubifs_tnc_next_ent(c, &key, &nm);
+ if (IS_ERR(dent)) {
+ err = PTR_ERR(dent);
+ goto out;
+ }
+
+ kfree(file->private_data);
+ file->f_pos = key_hash_flash(c, &dent->key);
+ file->private_data = dent;
+ cond_resched();
+ }
+
+out:
+ if (err != -ENOENT) {
+ ubifs_err(c, "cannot find next direntry, error %d", err);
+ return err;
+ }
+
+ kfree(file->private_data);
+ file->private_data = NULL;
+ file->f_pos = 2;
+ return 0;
+}
+
+static int ubifs_finddir(struct super_block *sb, char *dirname,
+ unsigned long root_inum, unsigned long *inum)
+{
+ int err;
+ struct qstr nm;
+ union ubifs_key key;
+ struct ubifs_dent_node *dent;
+ struct ubifs_info *c;
+ struct file *file;
+ struct dentry *dentry;
+ struct inode *dir;
+ int ret = 0;
+
+ file = kzalloc(sizeof(struct file), 0);
+ dentry = kzalloc(sizeof(struct dentry), 0);
+ dir = kzalloc(sizeof(struct inode), 0);
+ if (!file || !dentry || !dir) {
+ printf("%s: Error, no memory for malloc!\n", __func__);
+ err = -ENOMEM;
+ goto out;
+ }
+
+ dir->i_sb = sb;
+ file->f_path.dentry = dentry;
+ file->f_path.dentry->d_parent = dentry;
+ file->f_path.dentry->d_inode = dir;
+ file->f_path.dentry->d_inode->i_ino = root_inum;
+ c = sb->s_fs_info;
+
+ dbg_gen("dir ino %lu, f_pos %#llx", dir->i_ino, file->f_pos);
+
+ /* Find the first entry in TNC and save it */
+ lowest_dent_key(c, &key, dir->i_ino);
+ nm.name = NULL;
+ dent = ubifs_tnc_next_ent(c, &key, &nm);
+ if (IS_ERR(dent)) {
+ err = PTR_ERR(dent);
+ goto out;
+ }
+
+ file->f_pos = key_hash_flash(c, &dent->key);
+ file->private_data = dent;
+
+ while (1) {
+ dbg_gen("feed '%s', ino %llu, new f_pos %#x",
+ dent->name, (unsigned long long)le64_to_cpu(dent->inum),
+ key_hash_flash(c, &dent->key));
+#ifndef __UBOOT__
+ ubifs_assert(le64_to_cpu(dent->ch.sqnum) > ubifs_inode(dir)->creat_sqnum);
+#endif
+
+ nm.len = le16_to_cpu(dent->nlen);
+ if ((strncmp(dirname, (char *)dent->name, nm.len) == 0) &&
+ (strlen(dirname) == nm.len)) {
+ *inum = le64_to_cpu(dent->inum);
+ ret = 1;
+ goto out_free;
+ }
+
+ /* Switch to the next entry */
+ key_read(c, &dent->key, &key);
+ nm.name = (char *)dent->name;
+ dent = ubifs_tnc_next_ent(c, &key, &nm);
+ if (IS_ERR(dent)) {
+ err = PTR_ERR(dent);
+ goto out;
+ }
+
+ kfree(file->private_data);
+ file->f_pos = key_hash_flash(c, &dent->key);
+ file->private_data = dent;
+ cond_resched();
+ }
+
+out:
+ if (err != -ENOENT)
+ dbg_gen("cannot find next direntry, error %d", err);
+
+out_free:
+ kfree(file->private_data);
+ free(file);
+ free(dentry);
+ free(dir);
+
+ return ret;
+}
+
+static unsigned long ubifs_findfile(struct super_block *sb, char *filename)
+{
+ int ret;
+ char *next;
+ char fpath[128];
+ char symlinkpath[128];
+ char *name = fpath;
+ unsigned long root_inum = 1;
+ unsigned long inum;
+ int symlink_count = 0; /* Don't allow symlink recursion */
+ char link_name[64];
+
+ strcpy(fpath, filename);
+
+ /* Remove all leading slashes */
+ while (*name == '/')
+ name++;
+
+ /*
+ * Handle root-direcoty ('/')
+ */
+ inum = root_inum;
+ if (!name || *name == '\0')
+ return inum;
+
+ for (;;) {
+ struct inode *inode;
+ struct ubifs_inode *ui;
+
+ /* Extract the actual part from the pathname. */
+ next = strchr(name, '/');
+ if (next) {
+ /* Remove all leading slashes. */
+ while (*next == '/')
+ *(next++) = '\0';
+ }
+
+ ret = ubifs_finddir(sb, name, root_inum, &inum);
+ if (!ret)
+ return 0;
+ inode = ubifs_iget(sb, inum);
+
+ if (!inode)
+ return 0;
+ ui = ubifs_inode(inode);
+
+ if ((inode->i_mode & S_IFMT) == S_IFLNK) {
+ char buf[128];
+
+ /* We have some sort of symlink recursion, bail out */
+ if (symlink_count++ > 8) {
+ printf("Symlink recursion, aborting\n");
+ return 0;
+ }
+ memcpy(link_name, ui->data, ui->data_len);
+ link_name[ui->data_len] = '\0';
+
+ if (link_name[0] == '/') {
+ /* Absolute path, redo everything without
+ * the leading slash */
+ next = name = link_name + 1;
+ root_inum = 1;
+ continue;
+ }
+ /* Relative to cur dir */
+ sprintf(buf, "%s/%s",
+ link_name, next == NULL ? "" : next);
+ memcpy(symlinkpath, buf, sizeof(buf));
+ next = name = symlinkpath;
+ continue;
+ }
+
+ /*
+ * Check if directory with this name exists
+ */
+
+ /* Found the node! */
+ if (!next || *next == '\0')
+ return inum;
+
+ root_inum = inum;
+ name = next;
+ }
+
+ return 0;
+}
+
+int ubifs_set_blk_dev(struct blk_desc *rbdd, struct disk_partition *info)
+{
+ if (rbdd) {
+ debug("UBIFS cannot be used with normal block devices\n");
+ return -1;
+ }
+
+ /*
+ * Should never happen since blk_get_device_part_str() already checks
+ * this, but better safe then sorry.
+ */
+ if (!ubifs_is_mounted()) {
+ debug("UBIFS not mounted, use ubifsmount to mount volume first!\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int ubifs_ls(const char *filename)
+{
+ struct ubifs_info *c = ubifs_sb->s_fs_info;
+ struct file *file;
+ struct dentry *dentry;
+ struct inode *dir;
+ void *dirent = NULL;
+ unsigned long inum;
+ int ret = 0;
+
+ c->ubi = ubi_open_volume(c->vi.ubi_num, c->vi.vol_id, UBI_READONLY);
+ inum = ubifs_findfile(ubifs_sb, (char *)filename);
+ if (!inum) {
+ ret = -1;
+ goto out;
+ }
+
+ file = kzalloc(sizeof(struct file), 0);
+ dentry = kzalloc(sizeof(struct dentry), 0);
+ dir = kzalloc(sizeof(struct inode), 0);
+ if (!file || !dentry || !dir) {
+ printf("%s: Error, no memory for malloc!\n", __func__);
+ ret = -ENOMEM;
+ goto out_mem;
+ }
+
+ dir->i_sb = ubifs_sb;
+ file->f_path.dentry = dentry;
+ file->f_path.dentry->d_parent = dentry;
+ file->f_path.dentry->d_inode = dir;
+ file->f_path.dentry->d_inode->i_ino = inum;
+ file->f_pos = 1;
+ file->private_data = NULL;
+ ubifs_printdir(file, dirent);
+
+out_mem:
+ if (file)
+ free(file);
+ if (dentry)
+ free(dentry);
+ if (dir)
+ free(dir);
+
+out:
+ ubi_close_volume(c->ubi);
+ return ret;
+}
+
+int ubifs_exists(const char *filename)
+{
+ struct ubifs_info *c = ubifs_sb->s_fs_info;
+ unsigned long inum;
+
+ c->ubi = ubi_open_volume(c->vi.ubi_num, c->vi.vol_id, UBI_READONLY);
+ inum = ubifs_findfile(ubifs_sb, (char *)filename);
+ ubi_close_volume(c->ubi);
+
+ return inum != 0;
+}
+
+int ubifs_size(const char *filename, loff_t *size)
+{
+ struct ubifs_info *c = ubifs_sb->s_fs_info;
+ unsigned long inum;
+ struct inode *inode;
+ int err = 0;
+
+ c->ubi = ubi_open_volume(c->vi.ubi_num, c->vi.vol_id, UBI_READONLY);
+
+ inum = ubifs_findfile(ubifs_sb, (char *)filename);
+ if (!inum) {
+ err = -1;
+ goto out;
+ }
+
+ inode = ubifs_iget(ubifs_sb, inum);
+ if (IS_ERR(inode)) {
+ printf("%s: Error reading inode %ld!\n", __func__, inum);
+ err = PTR_ERR(inode);
+ goto out;
+ }
+
+ *size = inode->i_size;
+
+ ubifs_iput(inode);
+out:
+ ubi_close_volume(c->ubi);
+ return err;
+}
+
+/*
+ * ubifsload...
+ */
+
+/* file.c */
+
+static inline void *kmap(struct page *page)
+{
+ return page->addr;
+}
+
+static int read_block(struct inode *inode, void *addr, unsigned int block,
+ struct ubifs_data_node *dn)
+{
+ struct ubifs_info *c = inode->i_sb->s_fs_info;
+ int err, len, out_len;
+ union ubifs_key key;
+ unsigned int dlen;
+
+ data_key_init(c, &key, inode->i_ino, block);
+ err = ubifs_tnc_lookup(c, &key, dn);
+ if (err) {
+ if (err == -ENOENT)
+ /* Not found, so it must be a hole */
+ memset(addr, 0, UBIFS_BLOCK_SIZE);
+ return err;
+ }
+
+ ubifs_assert(le64_to_cpu(dn->ch.sqnum) > ubifs_inode(inode)->creat_sqnum);
+
+ len = le32_to_cpu(dn->size);
+ if (len <= 0 || len > UBIFS_BLOCK_SIZE)
+ goto dump;
+
+ dlen = le32_to_cpu(dn->ch.len) - UBIFS_DATA_NODE_SZ;
+ out_len = UBIFS_BLOCK_SIZE;
+ err = ubifs_decompress(c, &dn->data, dlen, addr, &out_len,
+ le16_to_cpu(dn->compr_type));
+ if (err || len != out_len)
+ goto dump;
+
+ /*
+ * Data length can be less than a full block, even for blocks that are
+ * not the last in the file (e.g., as a result of making a hole and
+ * appending data). Ensure that the remainder is zeroed out.
+ */
+ if (len < UBIFS_BLOCK_SIZE)
+ memset(addr + len, 0, UBIFS_BLOCK_SIZE - len);
+
+ return 0;
+
+dump:
+ ubifs_err(c, "bad data node (block %u, inode %lu)",
+ block, inode->i_ino);
+ ubifs_dump_node(c, dn);
+ return -EINVAL;
+}
+
+static int do_readpage(struct ubifs_info *c, struct inode *inode,
+ struct page *page, int last_block_size)
+{
+ void *addr;
+ int err = 0, i;
+ unsigned int block, beyond;
+ struct ubifs_data_node *dn;
+ loff_t i_size = inode->i_size;
+
+ dbg_gen("ino %lu, pg %lu, i_size %lld",
+ inode->i_ino, page->index, i_size);
+
+ addr = kmap(page);
+
+ block = page->index << UBIFS_BLOCKS_PER_PAGE_SHIFT;
+ beyond = (i_size + UBIFS_BLOCK_SIZE - 1) >> UBIFS_BLOCK_SHIFT;
+ if (block >= beyond) {
+ /* Reading beyond inode */
+ memset(addr, 0, PAGE_CACHE_SIZE);
+ goto out;
+ }
+
+ dn = kmalloc(UBIFS_MAX_DATA_NODE_SZ, GFP_NOFS);
+ if (!dn)
+ return -ENOMEM;
+
+ i = 0;
+ while (1) {
+ int ret;
+
+ if (block >= beyond) {
+ /* Reading beyond inode */
+ err = -ENOENT;
+ memset(addr, 0, UBIFS_BLOCK_SIZE);
+ } else {
+ /*
+ * Reading last block? Make sure to not write beyond
+ * the requested size in the destination buffer.
+ */
+ if (((block + 1) == beyond) || last_block_size) {
+ void *buff;
+ int dlen;
+
+ /*
+ * We need to buffer the data locally for the
+ * last block. This is to not pad the
+ * destination area to a multiple of
+ * UBIFS_BLOCK_SIZE.
+ */
+ buff = malloc_cache_aligned(UBIFS_BLOCK_SIZE);
+ if (!buff) {
+ printf("%s: Error, malloc fails!\n",
+ __func__);
+ err = -ENOMEM;
+ break;
+ }
+
+ /* Read block-size into temp buffer */
+ ret = read_block(inode, buff, block, dn);
+ if (ret) {
+ err = ret;
+ if (err != -ENOENT) {
+ free(buff);
+ break;
+ }
+ }
+
+ if (last_block_size)
+ dlen = last_block_size;
+ else
+ dlen = le32_to_cpu(dn->size);
+
+ /* Now copy required size back to dest */
+ memcpy(addr, buff, dlen);
+
+ free(buff);
+ } else {
+ ret = read_block(inode, addr, block, dn);
+ if (ret) {
+ err = ret;
+ if (err != -ENOENT)
+ break;
+ }
+ }
+ }
+ if (++i >= UBIFS_BLOCKS_PER_PAGE)
+ break;
+ block += 1;
+ addr += UBIFS_BLOCK_SIZE;
+ }
+ if (err) {
+ if (err == -ENOENT) {
+ /* Not found, so it must be a hole */
+ dbg_gen("hole");
+ goto out_free;
+ }
+ ubifs_err(c, "cannot read page %lu of inode %lu, error %d",
+ page->index, inode->i_ino, err);
+ goto error;
+ }
+
+out_free:
+ kfree(dn);
+out:
+ return 0;
+
+error:
+ kfree(dn);
+ return err;
+}
+
+int ubifs_read(const char *filename, void *buf, loff_t offset,
+ loff_t size, loff_t *actread)
+{
+ struct ubifs_info *c = ubifs_sb->s_fs_info;
+ unsigned long inum;
+ struct inode *inode;
+ struct page page;
+ int err = 0;
+ int i;
+ int count;
+ int last_block_size = 0;
+
+ *actread = 0;
+
+ if (offset & (PAGE_SIZE - 1)) {
+ printf("ubifs: Error offset must be a multiple of %d\n",
+ PAGE_SIZE);
+ return -1;
+ }
+
+ c->ubi = ubi_open_volume(c->vi.ubi_num, c->vi.vol_id, UBI_READONLY);
+ /* ubifs_findfile will resolve symlinks, so we know that we get
+ * the real file here */
+ inum = ubifs_findfile(ubifs_sb, (char *)filename);
+ if (!inum) {
+ err = -1;
+ goto out;
+ }
+
+ /*
+ * Read file inode
+ */
+ inode = ubifs_iget(ubifs_sb, inum);
+ if (IS_ERR(inode)) {
+ printf("%s: Error reading inode %ld!\n", __func__, inum);
+ err = PTR_ERR(inode);
+ goto out;
+ }
+
+ if (offset > inode->i_size) {
+ printf("ubifs: Error offset (%lld) > file-size (%lld)\n",
+ offset, size);
+ err = -1;
+ goto put_inode;
+ }
+
+ /*
+ * If no size was specified or if size bigger than filesize
+ * set size to filesize
+ */
+ if ((size == 0) || (size > (inode->i_size - offset)))
+ size = inode->i_size - offset;
+
+ count = (size + UBIFS_BLOCK_SIZE - 1) >> UBIFS_BLOCK_SHIFT;
+
+ page.addr = buf;
+ page.index = offset / PAGE_SIZE;
+ page.inode = inode;
+ for (i = 0; i < count; i++) {
+ /*
+ * Make sure to not read beyond the requested size
+ */
+ if (((i + 1) == count) && (size < inode->i_size))
+ last_block_size = size - (i * PAGE_SIZE);
+
+ err = do_readpage(c, inode, &page, last_block_size);
+ if (err)
+ break;
+
+ page.addr += PAGE_SIZE;
+ page.index++;
+ }
+
+ if (err) {
+ printf("Error reading file '%s'\n", filename);
+ *actread = i * PAGE_SIZE;
+ } else {
+ *actread = size;
+ }
+
+put_inode:
+ ubifs_iput(inode);
+
+out:
+ ubi_close_volume(c->ubi);
+ return err;
+}
+
+void ubifs_close(void)
+{
+}
+
+/* Compat wrappers for common/cmd_ubifs.c */
+int ubifs_load(char *filename, u32 addr, u32 size)
+{
+ loff_t actread;
+ int err;
+
+ printf("Loading file '%s' to addr 0x%08x...\n", filename, addr);
+
+ err = ubifs_read(filename, (void *)(uintptr_t)addr, 0, size, &actread);
+ if (err == 0) {
+ env_set_hex("filesize", actread);
+ printf("Done\n");
+ }
+
+ return err;
+}
+
+void uboot_ubifs_umount(void)
+{
+ if (ubifs_sb) {
+ printf("Unmounting UBIFS volume %s!\n",
+ ((struct ubifs_info *)(ubifs_sb->s_fs_info))->vi.name);
+ ubifs_umount(ubifs_sb->s_fs_info);
+ ubifs_sb = NULL;
+ }
+}
diff --git a/roms/u-boot/fs/ubifs/ubifs.h b/roms/u-boot/fs/ubifs/ubifs.h
new file mode 100644
index 000000000..512fdaa14
--- /dev/null
+++ b/roms/u-boot/fs/ubifs/ubifs.h
@@ -0,0 +1,2409 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation
+ *
+ * (C) Copyright 2008-2009
+ * Stefan Roese, DENX Software Engineering, sr@denx.de.
+ *
+ * Authors: Artem Bityutskiy (Битюцкий Артём)
+ * Adrian Hunter
+ */
+
+#ifndef __UBIFS_H__
+#define __UBIFS_H__
+
+#ifndef __UBOOT__
+#include <asm/div64.h>
+#include <linux/statfs.h>
+#include <linux/fs.h>
+#include <linux/err.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/rwsem.h>
+#include <linux/mtd/ubi.h>
+#include <linux/pagemap.h>
+#include <linux/backing-dev.h>
+#include <linux/security.h>
+#include "ubifs-media.h"
+#else
+#include <asm/atomic.h>
+#include <asm-generic/atomic-long.h>
+#include <ubi_uboot.h>
+#include <ubifs_uboot.h>
+
+#include <linux/ctype.h>
+#include <linux/time.h>
+#include <linux/math64.h>
+#include "ubifs-media.h"
+
+struct dentry;
+struct file;
+struct iattr;
+struct kstat;
+struct vfsmount;
+
+extern struct super_block *ubifs_sb;
+
+extern unsigned int ubifs_msg_flags;
+extern unsigned int ubifs_chk_flags;
+extern unsigned int ubifs_tst_flags;
+
+#define pgoff_t unsigned long
+
+/*
+ * We "simulate" the Linux page struct much simpler here
+ */
+struct page {
+ pgoff_t index;
+ void *addr;
+ struct inode *inode;
+};
+
+void iput(struct inode *inode);
+
+/* linux/include/time.h */
+#define NSEC_PER_SEC 1000000000L
+#define get_seconds() 0
+#define CURRENT_TIME_SEC ((struct timespec) { get_seconds(), 0 })
+
+struct timespec {
+ time_t tv_sec; /* seconds */
+ long tv_nsec; /* nanoseconds */
+};
+
+static struct timespec current_fs_time(struct super_block *sb)
+{
+ struct timespec now;
+ now.tv_sec = 0;
+ now.tv_nsec = 0;
+ return now;
+};
+
+/* linux/include/dcache.h */
+
+/*
+ * "quick string" -- eases parameter passing, but more importantly
+ * saves "metadata" about the string (ie length and the hash).
+ *
+ * hash comes first so it snuggles against d_parent in the
+ * dentry.
+ */
+struct qstr {
+ unsigned int hash;
+ unsigned int len;
+#ifndef __UBOOT__
+ const char *name;
+#else
+ char *name;
+#endif
+};
+
+/* include/linux/fs.h */
+
+/* Possible states of 'frozen' field */
+enum {
+ SB_UNFROZEN = 0, /* FS is unfrozen */
+ SB_FREEZE_WRITE = 1, /* Writes, dir ops, ioctls frozen */
+ SB_FREEZE_PAGEFAULT = 2, /* Page faults stopped as well */
+ SB_FREEZE_FS = 3, /* For internal FS use (e.g. to stop
+ * internal threads if needed) */
+ SB_FREEZE_COMPLETE = 4, /* ->freeze_fs finished successfully */
+};
+
+#define SB_FREEZE_LEVELS (SB_FREEZE_COMPLETE - 1)
+
+struct sb_writers {
+#ifndef __UBOOT__
+ /* Counters for counting writers at each level */
+ struct percpu_counter counter[SB_FREEZE_LEVELS];
+#endif
+ wait_queue_head_t wait; /* queue for waiting for
+ writers / faults to finish */
+ int frozen; /* Is sb frozen? */
+ wait_queue_head_t wait_unfrozen; /* queue for waiting for
+ sb to be thawed */
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+ struct lockdep_map lock_map[SB_FREEZE_LEVELS];
+#endif
+};
+
+struct address_space {
+ struct inode *host; /* owner: inode, block_device */
+#ifndef __UBOOT__
+ struct radix_tree_root page_tree; /* radix tree of all pages */
+#endif
+ spinlock_t tree_lock; /* and lock protecting it */
+ unsigned int i_mmap_writable;/* count VM_SHARED mappings */
+ struct rb_root i_mmap; /* tree of private and shared mappings */
+ struct list_head i_mmap_nonlinear;/*list VM_NONLINEAR mappings */
+ struct mutex i_mmap_mutex; /* protect tree, count, list */
+ /* Protected by tree_lock together with the radix tree */
+ unsigned long nrpages; /* number of total pages */
+ pgoff_t writeback_index;/* writeback starts here */
+ const struct address_space_operations *a_ops; /* methods */
+ unsigned long flags; /* error bits/gfp mask */
+#ifndef __UBOOT__
+ struct backing_dev_info *backing_dev_info; /* device readahead, etc */
+#endif
+ spinlock_t private_lock; /* for use by the address_space */
+ struct list_head private_list; /* ditto */
+ void *private_data; /* ditto */
+} __attribute__((aligned(sizeof(long))));
+
+/*
+ * Keep mostly read-only and often accessed (especially for
+ * the RCU path lookup and 'stat' data) fields at the beginning
+ * of the 'struct inode'
+ */
+struct inode {
+ umode_t i_mode;
+ unsigned short i_opflags;
+ kuid_t i_uid;
+ kgid_t i_gid;
+ unsigned int i_flags;
+
+#ifdef CONFIG_FS_POSIX_ACL
+ struct posix_acl *i_acl;
+ struct posix_acl *i_default_acl;
+#endif
+
+ const struct inode_operations *i_op;
+ struct super_block *i_sb;
+ struct address_space *i_mapping;
+
+#ifdef CONFIG_SECURITY
+ void *i_security;
+#endif
+
+ /* Stat data, not accessed from path walking */
+ unsigned long i_ino;
+ /*
+ * Filesystems may only read i_nlink directly. They shall use the
+ * following functions for modification:
+ *
+ * (set|clear|inc|drop)_nlink
+ * inode_(inc|dec)_link_count
+ */
+ union {
+ const unsigned int i_nlink;
+ unsigned int __i_nlink;
+ };
+ dev_t i_rdev;
+ loff_t i_size;
+ struct timespec i_atime;
+ struct timespec i_mtime;
+ struct timespec i_ctime;
+ spinlock_t i_lock; /* i_blocks, i_bytes, maybe i_size */
+ unsigned short i_bytes;
+ unsigned int i_blkbits;
+ blkcnt_t i_blocks;
+
+#ifdef __NEED_I_SIZE_ORDERED
+ seqcount_t i_size_seqcount;
+#endif
+
+ /* Misc */
+ unsigned long i_state;
+ struct mutex i_mutex;
+
+ unsigned long dirtied_when; /* jiffies of first dirtying */
+
+ struct hlist_node i_hash;
+ struct list_head i_wb_list; /* backing dev IO list */
+ struct list_head i_lru; /* inode LRU list */
+ struct list_head i_sb_list;
+ union {
+ struct hlist_head i_dentry;
+ struct rcu_head i_rcu;
+ };
+ u64 i_version;
+ atomic_t i_count;
+ atomic_t i_dio_count;
+ atomic_t i_writecount;
+ const struct file_operations *i_fop; /* former ->i_op->default_file_ops */
+ struct file_lock *i_flock;
+ struct address_space i_data;
+#ifdef CONFIG_QUOTA
+ struct dquot *i_dquot[MAXQUOTAS];
+#endif
+ struct list_head i_devices;
+ union {
+ struct pipe_inode_info *i_pipe;
+ struct block_device *i_bdev;
+ struct cdev *i_cdev;
+ };
+
+ __u32 i_generation;
+
+#ifdef CONFIG_FSNOTIFY
+ __u32 i_fsnotify_mask; /* all events this inode cares about */
+ struct hlist_head i_fsnotify_marks;
+#endif
+
+#ifdef CONFIG_IMA
+ atomic_t i_readcount; /* struct files open RO */
+#endif
+ void *i_private; /* fs or device private pointer */
+};
+
+struct super_operations {
+ struct inode *(*alloc_inode)(struct super_block *sb);
+ void (*destroy_inode)(struct inode *);
+
+ void (*dirty_inode) (struct inode *, int flags);
+ int (*write_inode) (struct inode *, struct writeback_control *wbc);
+ int (*drop_inode) (struct inode *);
+ void (*evict_inode) (struct inode *);
+ void (*put_super) (struct super_block *);
+ int (*sync_fs)(struct super_block *sb, int wait);
+ int (*freeze_fs) (struct super_block *);
+ int (*unfreeze_fs) (struct super_block *);
+#ifndef __UBOOT__
+ int (*statfs) (struct dentry *, struct kstatfs *);
+#endif
+ int (*remount_fs) (struct super_block *, int *, char *);
+ void (*umount_begin) (struct super_block *);
+
+#ifndef __UBOOT__
+ int (*show_options)(struct seq_file *, struct dentry *);
+ int (*show_devname)(struct seq_file *, struct dentry *);
+ int (*show_path)(struct seq_file *, struct dentry *);
+ int (*show_stats)(struct seq_file *, struct dentry *);
+#endif
+#ifdef CONFIG_QUOTA
+ ssize_t (*quota_read)(struct super_block *, int, char *, size_t, loff_t);
+ ssize_t (*quota_write)(struct super_block *, int, const char *, size_t, loff_t);
+#endif
+ int (*bdev_try_to_free_page)(struct super_block*, struct page*, gfp_t);
+ long (*nr_cached_objects)(struct super_block *, int);
+ long (*free_cached_objects)(struct super_block *, long, int);
+};
+
+struct super_block {
+ struct list_head s_list; /* Keep this first */
+ dev_t s_dev; /* search index; _not_ kdev_t */
+ unsigned char s_blocksize_bits;
+ unsigned long s_blocksize;
+ loff_t s_maxbytes; /* Max file size */
+ struct file_system_type *s_type;
+ const struct super_operations *s_op;
+ const struct dquot_operations *dq_op;
+ const struct quotactl_ops *s_qcop;
+ const struct export_operations *s_export_op;
+ unsigned long s_flags;
+ unsigned long s_magic;
+ struct dentry *s_root;
+ struct rw_semaphore s_umount;
+ int s_count;
+ atomic_t s_active;
+#ifdef CONFIG_SECURITY
+ void *s_security;
+#endif
+ const struct xattr_handler **s_xattr;
+
+ struct list_head s_inodes; /* all inodes */
+#ifndef __UBOOT__
+ struct hlist_bl_head s_anon; /* anonymous dentries for (nfs) exporting */
+#endif
+ struct list_head s_mounts; /* list of mounts; _not_ for fs use */
+ struct block_device *s_bdev;
+#ifndef __UBOOT__
+ struct backing_dev_info *s_bdi;
+#endif
+ struct mtd_info *s_mtd;
+#ifndef __UBOOT__
+ struct hlist_node s_instances;
+ struct quota_info s_dquot; /* Diskquota specific options */
+#endif
+
+ struct sb_writers s_writers;
+
+ char s_id[32]; /* Informational name */
+ u8 s_uuid[16]; /* UUID */
+
+ void *s_fs_info; /* Filesystem private info */
+ unsigned int s_max_links;
+#ifndef __UBOOT__
+ fmode_t s_mode;
+#endif
+
+ /* Granularity of c/m/atime in ns.
+ Cannot be worse than a second */
+ u32 s_time_gran;
+
+ /*
+ * The next field is for VFS *only*. No filesystems have any business
+ * even looking at it. You had been warned.
+ */
+ struct mutex s_vfs_rename_mutex; /* Kludge */
+
+ /*
+ * Filesystem subtype. If non-empty the filesystem type field
+ * in /proc/mounts will be "type.subtype"
+ */
+ char *s_subtype;
+
+#ifndef __UBOOT__
+ /*
+ * Saved mount options for lazy filesystems using
+ * generic_show_options()
+ */
+ char __rcu *s_options;
+#endif
+ const struct dentry_operations *s_d_op; /* default d_op for dentries */
+
+ /*
+ * Saved pool identifier for cleancache (-1 means none)
+ */
+ int cleancache_poolid;
+
+#ifndef __UBOOT__
+ struct shrinker s_shrink; /* per-sb shrinker handle */
+#endif
+
+ /* Number of inodes with nlink == 0 but still referenced */
+ atomic_long_t s_remove_count;
+
+ /* Being remounted read-only */
+ int s_readonly_remount;
+
+ /* AIO completions deferred from interrupt context */
+ struct workqueue_struct *s_dio_done_wq;
+
+#ifndef __UBOOT__
+ /*
+ * Keep the lru lists last in the structure so they always sit on their
+ * own individual cachelines.
+ */
+ struct list_lru s_dentry_lru ____cacheline_aligned_in_smp;
+ struct list_lru s_inode_lru ____cacheline_aligned_in_smp;
+#endif
+ struct rcu_head rcu;
+};
+
+struct file_system_type {
+ const char *name;
+ int fs_flags;
+#define FS_REQUIRES_DEV 1
+#define FS_BINARY_MOUNTDATA 2
+#define FS_HAS_SUBTYPE 4
+#define FS_USERNS_MOUNT 8 /* Can be mounted by userns root */
+#define FS_USERNS_DEV_MOUNT 16 /* A userns mount does not imply MNT_NODEV */
+#define FS_RENAME_DOES_D_MOVE 32768 /* FS will handle d_move() during rename() internally. */
+ struct dentry *(*mount) (struct file_system_type *, int,
+ const char *, void *);
+ void (*kill_sb) (struct super_block *);
+ struct module *owner;
+ struct file_system_type * next;
+ struct hlist_head fs_supers;
+
+#ifndef __UBOOT__
+ struct lock_class_key s_lock_key;
+ struct lock_class_key s_umount_key;
+ struct lock_class_key s_vfs_rename_key;
+ struct lock_class_key s_writers_key[SB_FREEZE_LEVELS];
+
+ struct lock_class_key i_lock_key;
+ struct lock_class_key i_mutex_key;
+ struct lock_class_key i_mutex_dir_key;
+#endif
+};
+
+/* include/linux/mount.h */
+struct vfsmount {
+ struct dentry *mnt_root; /* root of the mounted tree */
+ struct super_block *mnt_sb; /* pointer to superblock */
+ int mnt_flags;
+};
+
+struct path {
+ struct vfsmount *mnt;
+ struct dentry *dentry;
+};
+
+struct file {
+ struct path f_path;
+#define f_dentry f_path.dentry
+#define f_vfsmnt f_path.mnt
+ const struct file_operations *f_op;
+ unsigned int f_flags;
+ loff_t f_pos;
+ unsigned int f_uid, f_gid;
+
+ u64 f_version;
+#ifdef CONFIG_SECURITY
+ void *f_security;
+#endif
+ /* needed for tty driver, and maybe others */
+ void *private_data;
+
+#ifdef CONFIG_EPOLL
+ /* Used by fs/eventpoll.c to link all the hooks to this file */
+ struct list_head f_ep_links;
+ spinlock_t f_ep_lock;
+#endif /* #ifdef CONFIG_EPOLL */
+#ifdef CONFIG_DEBUG_WRITECOUNT
+ unsigned long f_mnt_write_state;
+#endif
+};
+
+/*
+ * get_seconds() not really needed in the read-only implmentation
+ */
+#define get_seconds() 0
+
+/* 4k page size */
+#define PAGE_CACHE_SHIFT 12
+#define PAGE_CACHE_SIZE (1 << PAGE_CACHE_SHIFT)
+
+/* Page cache limit. The filesystems should put that into their s_maxbytes
+ limits, otherwise bad things can happen in VM. */
+#if BITS_PER_LONG==32
+#define MAX_LFS_FILESIZE (((u64)PAGE_CACHE_SIZE << (BITS_PER_LONG-1))-1)
+#elif BITS_PER_LONG==64
+#define MAX_LFS_FILESIZE 0x7fffffffffffffffUL
+#endif
+
+/*
+ * These are the fs-independent mount-flags: up to 32 flags are supported
+ */
+#define MS_RDONLY 1 /* Mount read-only */
+#define MS_NOSUID 2 /* Ignore suid and sgid bits */
+#define MS_NODEV 4 /* Disallow access to device special files */
+#define MS_NOEXEC 8 /* Disallow program execution */
+#define MS_SYNCHRONOUS 16 /* Writes are synced at once */
+#define MS_REMOUNT 32 /* Alter flags of a mounted FS */
+#define MS_MANDLOCK 64 /* Allow mandatory locks on an FS */
+#define MS_DIRSYNC 128 /* Directory modifications are synchronous */
+#define MS_NOATIME 1024 /* Do not update access times. */
+#define MS_NODIRATIME 2048 /* Do not update directory access times */
+#define MS_BIND 4096
+#define MS_MOVE 8192
+#define MS_REC 16384
+#define MS_VERBOSE 32768 /* War is peace. Verbosity is silence.
+ MS_VERBOSE is deprecated. */
+#define MS_SILENT 32768
+#define MS_POSIXACL (1<<16) /* VFS does not apply the umask */
+#define MS_UNBINDABLE (1<<17) /* change to unbindable */
+#define MS_PRIVATE (1<<18) /* change to private */
+#define MS_SLAVE (1<<19) /* change to slave */
+#define MS_SHARED (1<<20) /* change to shared */
+#define MS_RELATIME (1<<21) /* Update atime relative to mtime/ctime. */
+#define MS_KERNMOUNT (1<<22) /* this is a kern_mount call */
+#define MS_I_VERSION (1<<23) /* Update inode I_version field */
+#define MS_ACTIVE (1<<30)
+#define MS_NOUSER (1<<31)
+
+#define I_NEW 8
+
+/* Inode flags - they have nothing to superblock flags now */
+
+#define S_SYNC 1 /* Writes are synced at once */
+#define S_NOATIME 2 /* Do not update access times */
+#define S_APPEND 4 /* Append-only file */
+#define S_IMMUTABLE 8 /* Immutable file */
+#define S_DEAD 16 /* removed, but still open directory */
+#define S_NOQUOTA 32 /* Inode is not counted to quota */
+#define S_DIRSYNC 64 /* Directory modifications are synchronous */
+#define S_NOCMTIME 128 /* Do not update file c/mtime */
+#define S_SWAPFILE 256 /* Do not truncate: swapon got its bmaps */
+#define S_PRIVATE 512 /* Inode is fs-internal */
+
+/* include/linux/stat.h */
+
+#define S_IFMT 00170000
+#define S_IFSOCK 0140000
+#define S_IFLNK 0120000
+#define S_IFREG 0100000
+#define S_IFBLK 0060000
+#define S_IFDIR 0040000
+#define S_IFCHR 0020000
+#define S_IFIFO 0010000
+#define S_ISUID 0004000
+#define S_ISGID 0002000
+#define S_ISVTX 0001000
+
+/* include/linux/fs.h */
+
+/*
+ * File types
+ *
+ * NOTE! These match bits 12..15 of stat.st_mode
+ * (ie "(i_mode >> 12) & 15").
+ */
+#define DT_UNKNOWN 0
+#define DT_FIFO 1
+#define DT_CHR 2
+#define DT_DIR 4
+#define DT_BLK 6
+#define DT_REG 8
+#define DT_LNK 10
+#define DT_SOCK 12
+#define DT_WHT 14
+
+#define I_DIRTY_SYNC 1
+#define I_DIRTY_DATASYNC 2
+#define I_DIRTY_PAGES 4
+#define I_NEW 8
+#define I_WILL_FREE 16
+#define I_FREEING 32
+#define I_CLEAR 64
+#define __I_LOCK 7
+#define I_LOCK (1 << __I_LOCK)
+#define __I_SYNC 8
+#define I_SYNC (1 << __I_SYNC)
+
+#define I_DIRTY (I_DIRTY_SYNC | I_DIRTY_DATASYNC | I_DIRTY_PAGES)
+
+/* linux/include/dcache.h */
+
+#define DNAME_INLINE_LEN_MIN 36
+
+struct dentry {
+ unsigned int d_flags; /* protected by d_lock */
+ spinlock_t d_lock; /* per dentry lock */
+ struct inode *d_inode; /* Where the name belongs to - NULL is
+ * negative */
+ /*
+ * The next three fields are touched by __d_lookup. Place them here
+ * so they all fit in a cache line.
+ */
+ struct hlist_node d_hash; /* lookup hash list */
+ struct dentry *d_parent; /* parent directory */
+ struct qstr d_name;
+
+ struct list_head d_lru; /* LRU list */
+ /*
+ * d_child and d_rcu can share memory
+ */
+ struct list_head d_subdirs; /* our children */
+ struct list_head d_alias; /* inode alias list */
+ unsigned long d_time; /* used by d_revalidate */
+ struct super_block *d_sb; /* The root of the dentry tree */
+ void *d_fsdata; /* fs-specific data */
+#ifdef CONFIG_PROFILING
+ struct dcookie_struct *d_cookie; /* cookie, if any */
+#endif
+ int d_mounted;
+ unsigned char d_iname[DNAME_INLINE_LEN_MIN]; /* small names */
+};
+
+static inline ino_t parent_ino(struct dentry *dentry)
+{
+ ino_t res;
+
+ spin_lock(&dentry->d_lock);
+ res = dentry->d_parent->d_inode->i_ino;
+ spin_unlock(&dentry->d_lock);
+ return res;
+}
+
+/* debug.c */
+
+#define module_param_named(...)
+
+/* misc.h */
+#define mutex_lock_nested(...)
+#define mutex_unlock_nested(...)
+#define mutex_is_locked(...) 1
+#endif
+
+/* Version of this UBIFS implementation */
+#define UBIFS_VERSION 1
+
+/* Normal UBIFS messages */
+#ifdef CONFIG_UBIFS_SILENCE_MSG
+#define ubifs_msg(c, fmt, ...)
+#else
+#define ubifs_msg(c, fmt, ...) \
+ pr_notice("UBIFS (ubi%d:%d): " fmt "\n", \
+ (c)->vi.ubi_num, (c)->vi.vol_id, ##__VA_ARGS__)
+#endif
+/* UBIFS error messages */
+#ifndef __UBOOT__
+#define ubifs_err(c, fmt, ...) \
+ pr_err("UBIFS error (ubi%d:%d pid %d): %s: " fmt "\n", \
+ (c)->vi.ubi_num, (c)->vi.vol_id, current->pid, \
+ __func__, ##__VA_ARGS__)
+/* UBIFS warning messages */
+#define ubifs_warn(c, fmt, ...) \
+ pr_warn("UBIFS warning (ubi%d:%d pid %d): %s: " fmt "\n", \
+ (c)->vi.ubi_num, (c)->vi.vol_id, current->pid, \
+ __func__, ##__VA_ARGS__)
+#else
+#define ubifs_err(c, fmt, ...) \
+ pr_err("UBIFS error (ubi%d:%d pid %d): %s: " fmt "\n", \
+ (c)->vi.ubi_num, (c)->vi.vol_id, 0, \
+ __func__, ##__VA_ARGS__)
+/* UBIFS warning messages */
+#define ubifs_warn(c, fmt, ...) \
+ pr_warn("UBIFS warning (ubi%d:%d pid %d): %s: " fmt "\n", \
+ (c)->vi.ubi_num, (c)->vi.vol_id, 0, \
+ __func__, ##__VA_ARGS__)
+
+#endif
+
+/*
+ * A variant of 'ubifs_err()' which takes the UBIFS file-sytem description
+ * object as an argument.
+ */
+#define ubifs_errc(c, fmt, ...) \
+ do { \
+ if (!(c)->probing) \
+ ubifs_err(c, fmt, ##__VA_ARGS__); \
+ } while (0)
+
+/* UBIFS file system VFS magic number */
+#define UBIFS_SUPER_MAGIC 0x24051905
+
+/* Number of UBIFS blocks per VFS page */
+#define UBIFS_BLOCKS_PER_PAGE (PAGE_CACHE_SIZE / UBIFS_BLOCK_SIZE)
+#define UBIFS_BLOCKS_PER_PAGE_SHIFT (PAGE_CACHE_SHIFT - UBIFS_BLOCK_SHIFT)
+
+/* "File system end of life" sequence number watermark */
+#define SQNUM_WARN_WATERMARK 0xFFFFFFFF00000000ULL
+#define SQNUM_WATERMARK 0xFFFFFFFFFF000000ULL
+
+/*
+ * Minimum amount of LEBs reserved for the index. At present the index needs at
+ * least 2 LEBs: one for the index head and one for in-the-gaps method (which
+ * currently does not cater for the index head and so excludes it from
+ * consideration).
+ */
+#define MIN_INDEX_LEBS 2
+
+/* Minimum amount of data UBIFS writes to the flash */
+#define MIN_WRITE_SZ (UBIFS_DATA_NODE_SZ + 8)
+
+/*
+ * Currently we do not support inode number overlapping and re-using, so this
+ * watermark defines dangerous inode number level. This should be fixed later,
+ * although it is difficult to exceed current limit. Another option is to use
+ * 64-bit inode numbers, but this means more overhead.
+ */
+#define INUM_WARN_WATERMARK 0xFFF00000
+#define INUM_WATERMARK 0xFFFFFF00
+
+/* Maximum number of entries in each LPT (LEB category) heap */
+#define LPT_HEAP_SZ 256
+
+/*
+ * Background thread name pattern. The numbers are UBI device and volume
+ * numbers.
+ */
+#define BGT_NAME_PATTERN "ubifs_bgt%d_%d"
+
+/* Write-buffer synchronization timeout interval in seconds */
+#define WBUF_TIMEOUT_SOFTLIMIT 3
+#define WBUF_TIMEOUT_HARDLIMIT 5
+
+/* Maximum possible inode number (only 32-bit inodes are supported now) */
+#define MAX_INUM 0xFFFFFFFF
+
+/* Number of non-data journal heads */
+#define NONDATA_JHEADS_CNT 2
+
+/* Shorter names for journal head numbers for internal usage */
+#define GCHD UBIFS_GC_HEAD
+#define BASEHD UBIFS_BASE_HEAD
+#define DATAHD UBIFS_DATA_HEAD
+
+/* 'No change' value for 'ubifs_change_lp()' */
+#define LPROPS_NC 0x80000001
+
+/*
+ * There is no notion of truncation key because truncation nodes do not exist
+ * in TNC. However, when replaying, it is handy to introduce fake "truncation"
+ * keys for truncation nodes because the code becomes simpler. So we define
+ * %UBIFS_TRUN_KEY type.
+ *
+ * But otherwise, out of the journal reply scope, the truncation keys are
+ * invalid.
+ */
+#define UBIFS_TRUN_KEY UBIFS_KEY_TYPES_CNT
+#define UBIFS_INVALID_KEY UBIFS_KEY_TYPES_CNT
+
+/*
+ * How much a directory entry/extended attribute entry adds to the parent/host
+ * inode.
+ */
+#define CALC_DENT_SIZE(name_len) ALIGN(UBIFS_DENT_NODE_SZ + (name_len) + 1, 8)
+
+/* How much an extended attribute adds to the host inode */
+#define CALC_XATTR_BYTES(data_len) ALIGN(UBIFS_INO_NODE_SZ + (data_len) + 1, 8)
+
+/*
+ * Znodes which were not touched for 'OLD_ZNODE_AGE' seconds are considered
+ * "old", and znode which were touched last 'YOUNG_ZNODE_AGE' seconds ago are
+ * considered "young". This is used by shrinker when selecting znode to trim
+ * off.
+ */
+#define OLD_ZNODE_AGE 20
+#define YOUNG_ZNODE_AGE 5
+
+/*
+ * Some compressors, like LZO, may end up with more data then the input buffer.
+ * So UBIFS always allocates larger output buffer, to be sure the compressor
+ * will not corrupt memory in case of worst case compression.
+ */
+#define WORST_COMPR_FACTOR 2
+
+/*
+ * How much memory is needed for a buffer where we compress a data node.
+ */
+#define COMPRESSED_DATA_NODE_BUF_SZ \
+ (UBIFS_DATA_NODE_SZ + UBIFS_BLOCK_SIZE * WORST_COMPR_FACTOR)
+
+/* Maximum expected tree height for use by bottom_up_buf */
+#define BOTTOM_UP_HEIGHT 64
+
+/* Maximum number of data nodes to bulk-read */
+#define UBIFS_MAX_BULK_READ 32
+
+/*
+ * Lockdep classes for UBIFS inode @ui_mutex.
+ */
+enum {
+ WB_MUTEX_1 = 0,
+ WB_MUTEX_2 = 1,
+ WB_MUTEX_3 = 2,
+};
+
+/*
+ * Znode flags (actually, bit numbers which store the flags).
+ *
+ * DIRTY_ZNODE: znode is dirty
+ * COW_ZNODE: znode is being committed and a new instance of this znode has to
+ * be created before changing this znode
+ * OBSOLETE_ZNODE: znode is obsolete, which means it was deleted, but it is
+ * still in the commit list and the ongoing commit operation
+ * will commit it, and delete this znode after it is done
+ */
+enum {
+ DIRTY_ZNODE = 0,
+ COW_ZNODE = 1,
+ OBSOLETE_ZNODE = 2,
+};
+
+/*
+ * Commit states.
+ *
+ * COMMIT_RESTING: commit is not wanted
+ * COMMIT_BACKGROUND: background commit has been requested
+ * COMMIT_REQUIRED: commit is required
+ * COMMIT_RUNNING_BACKGROUND: background commit is running
+ * COMMIT_RUNNING_REQUIRED: commit is running and it is required
+ * COMMIT_BROKEN: commit failed
+ */
+enum {
+ COMMIT_RESTING = 0,
+ COMMIT_BACKGROUND,
+ COMMIT_REQUIRED,
+ COMMIT_RUNNING_BACKGROUND,
+ COMMIT_RUNNING_REQUIRED,
+ COMMIT_BROKEN,
+};
+
+/*
+ * 'ubifs_scan_a_node()' return values.
+ *
+ * SCANNED_GARBAGE: scanned garbage
+ * SCANNED_EMPTY_SPACE: scanned empty space
+ * SCANNED_A_NODE: scanned a valid node
+ * SCANNED_A_CORRUPT_NODE: scanned a corrupted node
+ * SCANNED_A_BAD_PAD_NODE: scanned a padding node with invalid pad length
+ *
+ * Greater than zero means: 'scanned that number of padding bytes'
+ */
+enum {
+ SCANNED_GARBAGE = 0,
+ SCANNED_EMPTY_SPACE = -1,
+ SCANNED_A_NODE = -2,
+ SCANNED_A_CORRUPT_NODE = -3,
+ SCANNED_A_BAD_PAD_NODE = -4,
+};
+
+/*
+ * LPT cnode flag bits.
+ *
+ * DIRTY_CNODE: cnode is dirty
+ * OBSOLETE_CNODE: cnode is being committed and has been copied (or deleted),
+ * so it can (and must) be freed when the commit is finished
+ * COW_CNODE: cnode is being committed and must be copied before writing
+ */
+enum {
+ DIRTY_CNODE = 0,
+ OBSOLETE_CNODE = 1,
+ COW_CNODE = 2,
+};
+
+/*
+ * Dirty flag bits (lpt_drty_flgs) for LPT special nodes.
+ *
+ * LTAB_DIRTY: ltab node is dirty
+ * LSAVE_DIRTY: lsave node is dirty
+ */
+enum {
+ LTAB_DIRTY = 1,
+ LSAVE_DIRTY = 2,
+};
+
+/*
+ * Return codes used by the garbage collector.
+ * @LEB_FREED: the logical eraseblock was freed and is ready to use
+ * @LEB_FREED_IDX: indexing LEB was freed and can be used only after the commit
+ * @LEB_RETAINED: the logical eraseblock was freed and retained for GC purposes
+ */
+enum {
+ LEB_FREED,
+ LEB_FREED_IDX,
+ LEB_RETAINED,
+};
+
+/**
+ * struct ubifs_old_idx - index node obsoleted since last commit start.
+ * @rb: rb-tree node
+ * @lnum: LEB number of obsoleted index node
+ * @offs: offset of obsoleted index node
+ */
+struct ubifs_old_idx {
+ struct rb_node rb;
+ int lnum;
+ int offs;
+};
+
+/* The below union makes it easier to deal with keys */
+union ubifs_key {
+ uint8_t u8[UBIFS_SK_LEN];
+ uint32_t u32[UBIFS_SK_LEN/4];
+ uint64_t u64[UBIFS_SK_LEN/8];
+ __le32 j32[UBIFS_SK_LEN/4];
+};
+
+/**
+ * struct ubifs_scan_node - UBIFS scanned node information.
+ * @list: list of scanned nodes
+ * @key: key of node scanned (if it has one)
+ * @sqnum: sequence number
+ * @type: type of node scanned
+ * @offs: offset with LEB of node scanned
+ * @len: length of node scanned
+ * @node: raw node
+ */
+struct ubifs_scan_node {
+ struct list_head list;
+ union ubifs_key key;
+ unsigned long long sqnum;
+ int type;
+ int offs;
+ int len;
+ void *node;
+};
+
+/**
+ * struct ubifs_scan_leb - UBIFS scanned LEB information.
+ * @lnum: logical eraseblock number
+ * @nodes_cnt: number of nodes scanned
+ * @nodes: list of struct ubifs_scan_node
+ * @endpt: end point (and therefore the start of empty space)
+ * @buf: buffer containing entire LEB scanned
+ */
+struct ubifs_scan_leb {
+ int lnum;
+ int nodes_cnt;
+ struct list_head nodes;
+ int endpt;
+ void *buf;
+};
+
+/**
+ * struct ubifs_gced_idx_leb - garbage-collected indexing LEB.
+ * @list: list
+ * @lnum: LEB number
+ * @unmap: OK to unmap this LEB
+ *
+ * This data structure is used to temporary store garbage-collected indexing
+ * LEBs - they are not released immediately, but only after the next commit.
+ * This is needed to guarantee recoverability.
+ */
+struct ubifs_gced_idx_leb {
+ struct list_head list;
+ int lnum;
+ int unmap;
+};
+
+/**
+ * struct ubifs_inode - UBIFS in-memory inode description.
+ * @vfs_inode: VFS inode description object
+ * @creat_sqnum: sequence number at time of creation
+ * @del_cmtno: commit number corresponding to the time the inode was deleted,
+ * protected by @c->commit_sem;
+ * @xattr_size: summarized size of all extended attributes in bytes
+ * @xattr_cnt: count of extended attributes this inode has
+ * @xattr_names: sum of lengths of all extended attribute names belonging to
+ * this inode
+ * @dirty: non-zero if the inode is dirty
+ * @xattr: non-zero if this is an extended attribute inode
+ * @bulk_read: non-zero if bulk-read should be used
+ * @ui_mutex: serializes inode write-back with the rest of VFS operations,
+ * serializes "clean <-> dirty" state changes, serializes bulk-read,
+ * protects @dirty, @bulk_read, @ui_size, and @xattr_size
+ * @ui_lock: protects @synced_i_size
+ * @synced_i_size: synchronized size of inode, i.e. the value of inode size
+ * currently stored on the flash; used only for regular file
+ * inodes
+ * @ui_size: inode size used by UBIFS when writing to flash
+ * @flags: inode flags (@UBIFS_COMPR_FL, etc)
+ * @compr_type: default compression type used for this inode
+ * @last_page_read: page number of last page read (for bulk read)
+ * @read_in_a_row: number of consecutive pages read in a row (for bulk read)
+ * @data_len: length of the data attached to the inode
+ * @data: inode's data
+ *
+ * @ui_mutex exists for two main reasons. At first it prevents inodes from
+ * being written back while UBIFS changing them, being in the middle of an VFS
+ * operation. This way UBIFS makes sure the inode fields are consistent. For
+ * example, in 'ubifs_rename()' we change 3 inodes simultaneously, and
+ * write-back must not write any of them before we have finished.
+ *
+ * The second reason is budgeting - UBIFS has to budget all operations. If an
+ * operation is going to mark an inode dirty, it has to allocate budget for
+ * this. It cannot just mark it dirty because there is no guarantee there will
+ * be enough flash space to write the inode back later. This means UBIFS has
+ * to have full control over inode "clean <-> dirty" transitions (and pages
+ * actually). But unfortunately, VFS marks inodes dirty in many places, and it
+ * does not ask the file-system if it is allowed to do so (there is a notifier,
+ * but it is not enough), i.e., there is no mechanism to synchronize with this.
+ * So UBIFS has its own inode dirty flag and its own mutex to serialize
+ * "clean <-> dirty" transitions.
+ *
+ * The @synced_i_size field is used to make sure we never write pages which are
+ * beyond last synchronized inode size. See 'ubifs_writepage()' for more
+ * information.
+ *
+ * The @ui_size is a "shadow" variable for @inode->i_size and UBIFS uses
+ * @ui_size instead of @inode->i_size. The reason for this is that UBIFS cannot
+ * make sure @inode->i_size is always changed under @ui_mutex, because it
+ * cannot call 'truncate_setsize()' with @ui_mutex locked, because it would
+ * deadlock with 'ubifs_writepage()' (see file.c). All the other inode fields
+ * are changed under @ui_mutex, so they do not need "shadow" fields. Note, one
+ * could consider to rework locking and base it on "shadow" fields.
+ */
+struct ubifs_inode {
+ struct inode vfs_inode;
+ unsigned long long creat_sqnum;
+ unsigned long long del_cmtno;
+ unsigned int xattr_size;
+ unsigned int xattr_cnt;
+ unsigned int xattr_names;
+ unsigned int dirty:1;
+ unsigned int xattr:1;
+ unsigned int bulk_read:1;
+ unsigned int compr_type:2;
+ struct mutex ui_mutex;
+ spinlock_t ui_lock;
+ loff_t synced_i_size;
+ loff_t ui_size;
+ int flags;
+ pgoff_t last_page_read;
+ pgoff_t read_in_a_row;
+ int data_len;
+ void *data;
+};
+
+/**
+ * struct ubifs_unclean_leb - records a LEB recovered under read-only mode.
+ * @list: list
+ * @lnum: LEB number of recovered LEB
+ * @endpt: offset where recovery ended
+ *
+ * This structure records a LEB identified during recovery that needs to be
+ * cleaned but was not because UBIFS was mounted read-only. The information
+ * is used to clean the LEB when remounting to read-write mode.
+ */
+struct ubifs_unclean_leb {
+ struct list_head list;
+ int lnum;
+ int endpt;
+};
+
+/*
+ * LEB properties flags.
+ *
+ * LPROPS_UNCAT: not categorized
+ * LPROPS_DIRTY: dirty > free, dirty >= @c->dead_wm, not index
+ * LPROPS_DIRTY_IDX: dirty + free > @c->min_idx_node_sze and index
+ * LPROPS_FREE: free > 0, dirty < @c->dead_wm, not empty, not index
+ * LPROPS_HEAP_CNT: number of heaps used for storing categorized LEBs
+ * LPROPS_EMPTY: LEB is empty, not taken
+ * LPROPS_FREEABLE: free + dirty == leb_size, not index, not taken
+ * LPROPS_FRDI_IDX: free + dirty == leb_size and index, may be taken
+ * LPROPS_CAT_MASK: mask for the LEB categories above
+ * LPROPS_TAKEN: LEB was taken (this flag is not saved on the media)
+ * LPROPS_INDEX: LEB contains indexing nodes (this flag also exists on flash)
+ */
+enum {
+ LPROPS_UNCAT = 0,
+ LPROPS_DIRTY = 1,
+ LPROPS_DIRTY_IDX = 2,
+ LPROPS_FREE = 3,
+ LPROPS_HEAP_CNT = 3,
+ LPROPS_EMPTY = 4,
+ LPROPS_FREEABLE = 5,
+ LPROPS_FRDI_IDX = 6,
+ LPROPS_CAT_MASK = 15,
+ LPROPS_TAKEN = 16,
+ LPROPS_INDEX = 32,
+};
+
+/**
+ * struct ubifs_lprops - logical eraseblock properties.
+ * @free: amount of free space in bytes
+ * @dirty: amount of dirty space in bytes
+ * @flags: LEB properties flags (see above)
+ * @lnum: LEB number
+ * @list: list of same-category lprops (for LPROPS_EMPTY and LPROPS_FREEABLE)
+ * @hpos: heap position in heap of same-category lprops (other categories)
+ */
+struct ubifs_lprops {
+ int free;
+ int dirty;
+ int flags;
+ int lnum;
+ union {
+ struct list_head list;
+ int hpos;
+ };
+};
+
+/**
+ * struct ubifs_lpt_lprops - LPT logical eraseblock properties.
+ * @free: amount of free space in bytes
+ * @dirty: amount of dirty space in bytes
+ * @tgc: trivial GC flag (1 => unmap after commit end)
+ * @cmt: commit flag (1 => reserved for commit)
+ */
+struct ubifs_lpt_lprops {
+ int free;
+ int dirty;
+ unsigned tgc:1;
+ unsigned cmt:1;
+};
+
+/**
+ * struct ubifs_lp_stats - statistics of eraseblocks in the main area.
+ * @empty_lebs: number of empty LEBs
+ * @taken_empty_lebs: number of taken LEBs
+ * @idx_lebs: number of indexing LEBs
+ * @total_free: total free space in bytes (includes all LEBs)
+ * @total_dirty: total dirty space in bytes (includes all LEBs)
+ * @total_used: total used space in bytes (does not include index LEBs)
+ * @total_dead: total dead space in bytes (does not include index LEBs)
+ * @total_dark: total dark space in bytes (does not include index LEBs)
+ *
+ * The @taken_empty_lebs field counts the LEBs that are in the transient state
+ * of having been "taken" for use but not yet written to. @taken_empty_lebs is
+ * needed to account correctly for @gc_lnum, otherwise @empty_lebs could be
+ * used by itself (in which case 'unused_lebs' would be a better name). In the
+ * case of @gc_lnum, it is "taken" at mount time or whenever a LEB is retained
+ * by GC, but unlike other empty LEBs that are "taken", it may not be written
+ * straight away (i.e. before the next commit start or unmount), so either
+ * @gc_lnum must be specially accounted for, or the current approach followed
+ * i.e. count it under @taken_empty_lebs.
+ *
+ * @empty_lebs includes @taken_empty_lebs.
+ *
+ * @total_used, @total_dead and @total_dark fields do not account indexing
+ * LEBs.
+ */
+struct ubifs_lp_stats {
+ int empty_lebs;
+ int taken_empty_lebs;
+ int idx_lebs;
+ long long total_free;
+ long long total_dirty;
+ long long total_used;
+ long long total_dead;
+ long long total_dark;
+};
+
+struct ubifs_nnode;
+
+/**
+ * struct ubifs_cnode - LEB Properties Tree common node.
+ * @parent: parent nnode
+ * @cnext: next cnode to commit
+ * @flags: flags (%DIRTY_LPT_NODE or %OBSOLETE_LPT_NODE)
+ * @iip: index in parent
+ * @level: level in the tree (zero for pnodes, greater than zero for nnodes)
+ * @num: node number
+ */
+struct ubifs_cnode {
+ struct ubifs_nnode *parent;
+ struct ubifs_cnode *cnext;
+ unsigned long flags;
+ int iip;
+ int level;
+ int num;
+};
+
+/**
+ * struct ubifs_pnode - LEB Properties Tree leaf node.
+ * @parent: parent nnode
+ * @cnext: next cnode to commit
+ * @flags: flags (%DIRTY_LPT_NODE or %OBSOLETE_LPT_NODE)
+ * @iip: index in parent
+ * @level: level in the tree (always zero for pnodes)
+ * @num: node number
+ * @lprops: LEB properties array
+ */
+struct ubifs_pnode {
+ struct ubifs_nnode *parent;
+ struct ubifs_cnode *cnext;
+ unsigned long flags;
+ int iip;
+ int level;
+ int num;
+ struct ubifs_lprops lprops[UBIFS_LPT_FANOUT];
+};
+
+/**
+ * struct ubifs_nbranch - LEB Properties Tree internal node branch.
+ * @lnum: LEB number of child
+ * @offs: offset of child
+ * @nnode: nnode child
+ * @pnode: pnode child
+ * @cnode: cnode child
+ */
+struct ubifs_nbranch {
+ int lnum;
+ int offs;
+ union {
+ struct ubifs_nnode *nnode;
+ struct ubifs_pnode *pnode;
+ struct ubifs_cnode *cnode;
+ };
+};
+
+/**
+ * struct ubifs_nnode - LEB Properties Tree internal node.
+ * @parent: parent nnode
+ * @cnext: next cnode to commit
+ * @flags: flags (%DIRTY_LPT_NODE or %OBSOLETE_LPT_NODE)
+ * @iip: index in parent
+ * @level: level in the tree (always greater than zero for nnodes)
+ * @num: node number
+ * @nbranch: branches to child nodes
+ */
+struct ubifs_nnode {
+ struct ubifs_nnode *parent;
+ struct ubifs_cnode *cnext;
+ unsigned long flags;
+ int iip;
+ int level;
+ int num;
+ struct ubifs_nbranch nbranch[UBIFS_LPT_FANOUT];
+};
+
+/**
+ * struct ubifs_lpt_heap - heap of categorized lprops.
+ * @arr: heap array
+ * @cnt: number in heap
+ * @max_cnt: maximum number allowed in heap
+ *
+ * There are %LPROPS_HEAP_CNT heaps.
+ */
+struct ubifs_lpt_heap {
+ struct ubifs_lprops **arr;
+ int cnt;
+ int max_cnt;
+};
+
+/*
+ * Return codes for LPT scan callback function.
+ *
+ * LPT_SCAN_CONTINUE: continue scanning
+ * LPT_SCAN_ADD: add the LEB properties scanned to the tree in memory
+ * LPT_SCAN_STOP: stop scanning
+ */
+enum {
+ LPT_SCAN_CONTINUE = 0,
+ LPT_SCAN_ADD = 1,
+ LPT_SCAN_STOP = 2,
+};
+
+struct ubifs_info;
+
+/* Callback used by the 'ubifs_lpt_scan_nolock()' function */
+typedef int (*ubifs_lpt_scan_callback)(struct ubifs_info *c,
+ const struct ubifs_lprops *lprops,
+ int in_tree, void *data);
+
+/**
+ * struct ubifs_wbuf - UBIFS write-buffer.
+ * @c: UBIFS file-system description object
+ * @buf: write-buffer (of min. flash I/O unit size)
+ * @lnum: logical eraseblock number the write-buffer points to
+ * @offs: write-buffer offset in this logical eraseblock
+ * @avail: number of bytes available in the write-buffer
+ * @used: number of used bytes in the write-buffer
+ * @size: write-buffer size (in [@c->min_io_size, @c->max_write_size] range)
+ * @jhead: journal head the mutex belongs to (note, needed only to shut lockdep
+ * up by 'mutex_lock_nested()).
+ * @sync_callback: write-buffer synchronization callback
+ * @io_mutex: serializes write-buffer I/O
+ * @lock: serializes @buf, @lnum, @offs, @avail, @used, @next_ino and @inodes
+ * fields
+ * @softlimit: soft write-buffer timeout interval
+ * @delta: hard and soft timeouts delta (the timer expire interval is @softlimit
+ * and @softlimit + @delta)
+ * @timer: write-buffer timer
+ * @no_timer: non-zero if this write-buffer does not have a timer
+ * @need_sync: non-zero if the timer expired and the wbuf needs sync'ing
+ * @next_ino: points to the next position of the following inode number
+ * @inodes: stores the inode numbers of the nodes which are in wbuf
+ *
+ * The write-buffer synchronization callback is called when the write-buffer is
+ * synchronized in order to notify how much space was wasted due to
+ * write-buffer padding and how much free space is left in the LEB.
+ *
+ * Note: the fields @buf, @lnum, @offs, @avail and @used can be read under
+ * spin-lock or mutex because they are written under both mutex and spin-lock.
+ * @buf is appended to under mutex but overwritten under both mutex and
+ * spin-lock. Thus the data between @buf and @buf + @used can be read under
+ * spinlock.
+ */
+struct ubifs_wbuf {
+ struct ubifs_info *c;
+ void *buf;
+ int lnum;
+ int offs;
+ int avail;
+ int used;
+ int size;
+ int jhead;
+ int (*sync_callback)(struct ubifs_info *c, int lnum, int free, int pad);
+ struct mutex io_mutex;
+ spinlock_t lock;
+// ktime_t softlimit;
+// unsigned long long delta;
+// struct hrtimer timer;
+ unsigned int no_timer:1;
+ unsigned int need_sync:1;
+ int next_ino;
+ ino_t *inodes;
+};
+
+/**
+ * struct ubifs_bud - bud logical eraseblock.
+ * @lnum: logical eraseblock number
+ * @start: where the (uncommitted) bud data starts
+ * @jhead: journal head number this bud belongs to
+ * @list: link in the list buds belonging to the same journal head
+ * @rb: link in the tree of all buds
+ */
+struct ubifs_bud {
+ int lnum;
+ int start;
+ int jhead;
+ struct list_head list;
+ struct rb_node rb;
+};
+
+/**
+ * struct ubifs_jhead - journal head.
+ * @wbuf: head's write-buffer
+ * @buds_list: list of bud LEBs belonging to this journal head
+ * @grouped: non-zero if UBIFS groups nodes when writing to this journal head
+ *
+ * Note, the @buds list is protected by the @c->buds_lock.
+ */
+struct ubifs_jhead {
+ struct ubifs_wbuf wbuf;
+ struct list_head buds_list;
+ unsigned int grouped:1;
+};
+
+/**
+ * struct ubifs_zbranch - key/coordinate/length branch stored in znodes.
+ * @key: key
+ * @znode: znode address in memory
+ * @lnum: LEB number of the target node (indexing node or data node)
+ * @offs: target node offset within @lnum
+ * @len: target node length
+ */
+struct ubifs_zbranch {
+ union ubifs_key key;
+ union {
+ struct ubifs_znode *znode;
+ void *leaf;
+ };
+ int lnum;
+ int offs;
+ int len;
+};
+
+/**
+ * struct ubifs_znode - in-memory representation of an indexing node.
+ * @parent: parent znode or NULL if it is the root
+ * @cnext: next znode to commit
+ * @flags: znode flags (%DIRTY_ZNODE, %COW_ZNODE or %OBSOLETE_ZNODE)
+ * @time: last access time (seconds)
+ * @level: level of the entry in the TNC tree
+ * @child_cnt: count of child znodes
+ * @iip: index in parent's zbranch array
+ * @alt: lower bound of key range has altered i.e. child inserted at slot 0
+ * @lnum: LEB number of the corresponding indexing node
+ * @offs: offset of the corresponding indexing node
+ * @len: length of the corresponding indexing node
+ * @zbranch: array of znode branches (@c->fanout elements)
+ *
+ * Note! The @lnum, @offs, and @len fields are not really needed - we have them
+ * only for internal consistency check. They could be removed to save some RAM.
+ */
+struct ubifs_znode {
+ struct ubifs_znode *parent;
+ struct ubifs_znode *cnext;
+ unsigned long flags;
+ unsigned long time;
+ int level;
+ int child_cnt;
+ int iip;
+ int alt;
+ int lnum;
+ int offs;
+ int len;
+ struct ubifs_zbranch zbranch[];
+};
+
+/**
+ * struct bu_info - bulk-read information.
+ * @key: first data node key
+ * @zbranch: zbranches of data nodes to bulk read
+ * @buf: buffer to read into
+ * @buf_len: buffer length
+ * @gc_seq: GC sequence number to detect races with GC
+ * @cnt: number of data nodes for bulk read
+ * @blk_cnt: number of data blocks including holes
+ * @oef: end of file reached
+ */
+struct bu_info {
+ union ubifs_key key;
+ struct ubifs_zbranch zbranch[UBIFS_MAX_BULK_READ];
+ void *buf;
+ int buf_len;
+ int gc_seq;
+ int cnt;
+ int blk_cnt;
+ int eof;
+};
+
+/**
+ * struct ubifs_node_range - node length range description data structure.
+ * @len: fixed node length
+ * @min_len: minimum possible node length
+ * @max_len: maximum possible node length
+ *
+ * If @max_len is %0, the node has fixed length @len.
+ */
+struct ubifs_node_range {
+ union {
+ int len;
+ int min_len;
+ };
+ int max_len;
+};
+
+/**
+ * struct ubifs_compressor - UBIFS compressor description structure.
+ * @compr_type: compressor type (%UBIFS_COMPR_LZO, etc)
+ * @cc: cryptoapi compressor handle
+ * @comp_mutex: mutex used during compression
+ * @decomp_mutex: mutex used during decompression
+ * @name: compressor name
+ * @capi_name: cryptoapi compressor name
+ */
+struct ubifs_compressor {
+ int compr_type;
+ struct crypto_comp *cc;
+ struct mutex *comp_mutex;
+ struct mutex *decomp_mutex;
+ const char *name;
+ const char *capi_name;
+#ifdef __UBOOT__
+ int (*decompress)(const unsigned char *in, size_t in_len,
+ unsigned char *out, size_t *out_len);
+#endif
+};
+
+/**
+ * struct ubifs_budget_req - budget requirements of an operation.
+ *
+ * @fast: non-zero if the budgeting should try to acquire budget quickly and
+ * should not try to call write-back
+ * @recalculate: non-zero if @idx_growth, @data_growth, and @dd_growth fields
+ * have to be re-calculated
+ * @new_page: non-zero if the operation adds a new page
+ * @dirtied_page: non-zero if the operation makes a page dirty
+ * @new_dent: non-zero if the operation adds a new directory entry
+ * @mod_dent: non-zero if the operation removes or modifies an existing
+ * directory entry
+ * @new_ino: non-zero if the operation adds a new inode
+ * @new_ino_d: now much data newly created inode contains
+ * @dirtied_ino: how many inodes the operation makes dirty
+ * @dirtied_ino_d: now much data dirtied inode contains
+ * @idx_growth: how much the index will supposedly grow
+ * @data_growth: how much new data the operation will supposedly add
+ * @dd_growth: how much data that makes other data dirty the operation will
+ * supposedly add
+ *
+ * @idx_growth, @data_growth and @dd_growth are not used in budget request. The
+ * budgeting subsystem caches index and data growth values there to avoid
+ * re-calculating them when the budget is released. However, if @idx_growth is
+ * %-1, it is calculated by the release function using other fields.
+ *
+ * An inode may contain 4KiB of data at max., thus the widths of @new_ino_d
+ * is 13 bits, and @dirtied_ino_d - 15, because up to 4 inodes may be made
+ * dirty by the re-name operation.
+ *
+ * Note, UBIFS aligns node lengths to 8-bytes boundary, so the requester has to
+ * make sure the amount of inode data which contribute to @new_ino_d and
+ * @dirtied_ino_d fields are aligned.
+ */
+struct ubifs_budget_req {
+ unsigned int fast:1;
+ unsigned int recalculate:1;
+#ifndef UBIFS_DEBUG
+ unsigned int new_page:1;
+ unsigned int dirtied_page:1;
+ unsigned int new_dent:1;
+ unsigned int mod_dent:1;
+ unsigned int new_ino:1;
+ unsigned int new_ino_d:13;
+ unsigned int dirtied_ino:4;
+ unsigned int dirtied_ino_d:15;
+#else
+ /* Not bit-fields to check for overflows */
+ unsigned int new_page;
+ unsigned int dirtied_page;
+ unsigned int new_dent;
+ unsigned int mod_dent;
+ unsigned int new_ino;
+ unsigned int new_ino_d;
+ unsigned int dirtied_ino;
+ unsigned int dirtied_ino_d;
+#endif
+ int idx_growth;
+ int data_growth;
+ int dd_growth;
+};
+
+/**
+ * struct ubifs_orphan - stores the inode number of an orphan.
+ * @rb: rb-tree node of rb-tree of orphans sorted by inode number
+ * @list: list head of list of orphans in order added
+ * @new_list: list head of list of orphans added since the last commit
+ * @cnext: next orphan to commit
+ * @dnext: next orphan to delete
+ * @inum: inode number
+ * @new: %1 => added since the last commit, otherwise %0
+ * @cmt: %1 => commit pending, otherwise %0
+ * @del: %1 => delete pending, otherwise %0
+ */
+struct ubifs_orphan {
+ struct rb_node rb;
+ struct list_head list;
+ struct list_head new_list;
+ struct ubifs_orphan *cnext;
+ struct ubifs_orphan *dnext;
+ ino_t inum;
+ unsigned new:1;
+ unsigned cmt:1;
+ unsigned del:1;
+};
+
+/**
+ * struct ubifs_mount_opts - UBIFS-specific mount options information.
+ * @unmount_mode: selected unmount mode (%0 default, %1 normal, %2 fast)
+ * @bulk_read: enable/disable bulk-reads (%0 default, %1 disable, %2 enable)
+ * @chk_data_crc: enable/disable CRC data checking when reading data nodes
+ * (%0 default, %1 disable, %2 enable)
+ * @override_compr: override default compressor (%0 - do not override and use
+ * superblock compressor, %1 - override and use compressor
+ * specified in @compr_type)
+ * @compr_type: compressor type to override the superblock compressor with
+ * (%UBIFS_COMPR_NONE, etc)
+ */
+struct ubifs_mount_opts {
+ unsigned int unmount_mode:2;
+ unsigned int bulk_read:2;
+ unsigned int chk_data_crc:2;
+ unsigned int override_compr:1;
+ unsigned int compr_type:2;
+};
+
+/**
+ * struct ubifs_budg_info - UBIFS budgeting information.
+ * @idx_growth: amount of bytes budgeted for index growth
+ * @data_growth: amount of bytes budgeted for cached data
+ * @dd_growth: amount of bytes budgeted for cached data that will make
+ * other data dirty
+ * @uncommitted_idx: amount of bytes were budgeted for growth of the index, but
+ * which still have to be taken into account because the index
+ * has not been committed so far
+ * @old_idx_sz: size of index on flash
+ * @min_idx_lebs: minimum number of LEBs required for the index
+ * @nospace: non-zero if the file-system does not have flash space (used as
+ * optimization)
+ * @nospace_rp: the same as @nospace, but additionally means that even reserved
+ * pool is full
+ * @page_budget: budget for a page (constant, never changed after mount)
+ * @inode_budget: budget for an inode (constant, never changed after mount)
+ * @dent_budget: budget for a directory entry (constant, never changed after
+ * mount)
+ */
+struct ubifs_budg_info {
+ long long idx_growth;
+ long long data_growth;
+ long long dd_growth;
+ long long uncommitted_idx;
+ unsigned long long old_idx_sz;
+ int min_idx_lebs;
+ unsigned int nospace:1;
+ unsigned int nospace_rp:1;
+ int page_budget;
+ int inode_budget;
+ int dent_budget;
+};
+
+struct ubifs_debug_info;
+
+/**
+ * struct ubifs_info - UBIFS file-system description data structure
+ * (per-superblock).
+ * @vfs_sb: VFS @struct super_block object
+ * @bdi: backing device info object to make VFS happy and disable read-ahead
+ *
+ * @highest_inum: highest used inode number
+ * @max_sqnum: current global sequence number
+ * @cmt_no: commit number of the last successfully completed commit, protected
+ * by @commit_sem
+ * @cnt_lock: protects @highest_inum and @max_sqnum counters
+ * @fmt_version: UBIFS on-flash format version
+ * @ro_compat_version: R/O compatibility version
+ * @uuid: UUID from super block
+ *
+ * @lhead_lnum: log head logical eraseblock number
+ * @lhead_offs: log head offset
+ * @ltail_lnum: log tail logical eraseblock number (offset is always 0)
+ * @log_mutex: protects the log, @lhead_lnum, @lhead_offs, @ltail_lnum, and
+ * @bud_bytes
+ * @min_log_bytes: minimum required number of bytes in the log
+ * @cmt_bud_bytes: used during commit to temporarily amount of bytes in
+ * committed buds
+ *
+ * @buds: tree of all buds indexed by bud LEB number
+ * @bud_bytes: how many bytes of flash is used by buds
+ * @buds_lock: protects the @buds tree, @bud_bytes, and per-journal head bud
+ * lists
+ * @jhead_cnt: count of journal heads
+ * @jheads: journal heads (head zero is base head)
+ * @max_bud_bytes: maximum number of bytes allowed in buds
+ * @bg_bud_bytes: number of bud bytes when background commit is initiated
+ * @old_buds: buds to be released after commit ends
+ * @max_bud_cnt: maximum number of buds
+ *
+ * @commit_sem: synchronizes committer with other processes
+ * @cmt_state: commit state
+ * @cs_lock: commit state lock
+ * @cmt_wq: wait queue to sleep on if the log is full and a commit is running
+ *
+ * @big_lpt: flag that LPT is too big to write whole during commit
+ * @space_fixup: flag indicating that free space in LEBs needs to be cleaned up
+ * @no_chk_data_crc: do not check CRCs when reading data nodes (except during
+ * recovery)
+ * @bulk_read: enable bulk-reads
+ * @default_compr: default compression algorithm (%UBIFS_COMPR_LZO, etc)
+ * @rw_incompat: the media is not R/W compatible
+ *
+ * @tnc_mutex: protects the Tree Node Cache (TNC), @zroot, @cnext, @enext, and
+ * @calc_idx_sz
+ * @zroot: zbranch which points to the root index node and znode
+ * @cnext: next znode to commit
+ * @enext: next znode to commit to empty space
+ * @gap_lebs: array of LEBs used by the in-gaps commit method
+ * @cbuf: commit buffer
+ * @ileb_buf: buffer for commit in-the-gaps method
+ * @ileb_len: length of data in ileb_buf
+ * @ihead_lnum: LEB number of index head
+ * @ihead_offs: offset of index head
+ * @ilebs: pre-allocated index LEBs
+ * @ileb_cnt: number of pre-allocated index LEBs
+ * @ileb_nxt: next pre-allocated index LEBs
+ * @old_idx: tree of index nodes obsoleted since the last commit start
+ * @bottom_up_buf: a buffer which is used by 'dirty_cow_bottom_up()' in tnc.c
+ *
+ * @mst_node: master node
+ * @mst_offs: offset of valid master node
+ *
+ * @max_bu_buf_len: maximum bulk-read buffer length
+ * @bu_mutex: protects the pre-allocated bulk-read buffer and @c->bu
+ * @bu: pre-allocated bulk-read information
+ *
+ * @write_reserve_mutex: protects @write_reserve_buf
+ * @write_reserve_buf: on the write path we allocate memory, which might
+ * sometimes be unavailable, in which case we use this
+ * write reserve buffer
+ *
+ * @log_lebs: number of logical eraseblocks in the log
+ * @log_bytes: log size in bytes
+ * @log_last: last LEB of the log
+ * @lpt_lebs: number of LEBs used for lprops table
+ * @lpt_first: first LEB of the lprops table area
+ * @lpt_last: last LEB of the lprops table area
+ * @orph_lebs: number of LEBs used for the orphan area
+ * @orph_first: first LEB of the orphan area
+ * @orph_last: last LEB of the orphan area
+ * @main_lebs: count of LEBs in the main area
+ * @main_first: first LEB of the main area
+ * @main_bytes: main area size in bytes
+ *
+ * @key_hash_type: type of the key hash
+ * @key_hash: direntry key hash function
+ * @key_fmt: key format
+ * @key_len: key length
+ * @fanout: fanout of the index tree (number of links per indexing node)
+ *
+ * @min_io_size: minimal input/output unit size
+ * @min_io_shift: number of bits in @min_io_size minus one
+ * @max_write_size: maximum amount of bytes the underlying flash can write at a
+ * time (MTD write buffer size)
+ * @max_write_shift: number of bits in @max_write_size minus one
+ * @leb_size: logical eraseblock size in bytes
+ * @leb_start: starting offset of logical eraseblocks within physical
+ * eraseblocks
+ * @half_leb_size: half LEB size
+ * @idx_leb_size: how many bytes of an LEB are effectively available when it is
+ * used to store indexing nodes (@leb_size - @max_idx_node_sz)
+ * @leb_cnt: count of logical eraseblocks
+ * @max_leb_cnt: maximum count of logical eraseblocks
+ * @old_leb_cnt: count of logical eraseblocks before re-size
+ * @ro_media: the underlying UBI volume is read-only
+ * @ro_mount: the file-system was mounted as read-only
+ * @ro_error: UBIFS switched to R/O mode because an error happened
+ *
+ * @dirty_pg_cnt: number of dirty pages (not used)
+ * @dirty_zn_cnt: number of dirty znodes
+ * @clean_zn_cnt: number of clean znodes
+ *
+ * @space_lock: protects @bi and @lst
+ * @lst: lprops statistics
+ * @bi: budgeting information
+ * @calc_idx_sz: temporary variable which is used to calculate new index size
+ * (contains accurate new index size at end of TNC commit start)
+ *
+ * @ref_node_alsz: size of the LEB reference node aligned to the min. flash
+ * I/O unit
+ * @mst_node_alsz: master node aligned size
+ * @min_idx_node_sz: minimum indexing node aligned on 8-bytes boundary
+ * @max_idx_node_sz: maximum indexing node aligned on 8-bytes boundary
+ * @max_inode_sz: maximum possible inode size in bytes
+ * @max_znode_sz: size of znode in bytes
+ *
+ * @leb_overhead: how many bytes are wasted in an LEB when it is filled with
+ * data nodes of maximum size - used in free space reporting
+ * @dead_wm: LEB dead space watermark
+ * @dark_wm: LEB dark space watermark
+ * @block_cnt: count of 4KiB blocks on the FS
+ *
+ * @ranges: UBIFS node length ranges
+ * @ubi: UBI volume descriptor
+ * @di: UBI device information
+ * @vi: UBI volume information
+ *
+ * @orph_tree: rb-tree of orphan inode numbers
+ * @orph_list: list of orphan inode numbers in order added
+ * @orph_new: list of orphan inode numbers added since last commit
+ * @orph_cnext: next orphan to commit
+ * @orph_dnext: next orphan to delete
+ * @orphan_lock: lock for orph_tree and orph_new
+ * @orph_buf: buffer for orphan nodes
+ * @new_orphans: number of orphans since last commit
+ * @cmt_orphans: number of orphans being committed
+ * @tot_orphans: number of orphans in the rb_tree
+ * @max_orphans: maximum number of orphans allowed
+ * @ohead_lnum: orphan head LEB number
+ * @ohead_offs: orphan head offset
+ * @no_orphs: non-zero if there are no orphans
+ *
+ * @bgt: UBIFS background thread
+ * @bgt_name: background thread name
+ * @need_bgt: if background thread should run
+ * @need_wbuf_sync: if write-buffers have to be synchronized
+ *
+ * @gc_lnum: LEB number used for garbage collection
+ * @sbuf: a buffer of LEB size used by GC and replay for scanning
+ * @idx_gc: list of index LEBs that have been garbage collected
+ * @idx_gc_cnt: number of elements on the idx_gc list
+ * @gc_seq: incremented for every non-index LEB garbage collected
+ * @gced_lnum: last non-index LEB that was garbage collected
+ *
+ * @infos_list: links all 'ubifs_info' objects
+ * @umount_mutex: serializes shrinker and un-mount
+ * @shrinker_run_no: shrinker run number
+ *
+ * @space_bits: number of bits needed to record free or dirty space
+ * @lpt_lnum_bits: number of bits needed to record a LEB number in the LPT
+ * @lpt_offs_bits: number of bits needed to record an offset in the LPT
+ * @lpt_spc_bits: number of bits needed to space in the LPT
+ * @pcnt_bits: number of bits needed to record pnode or nnode number
+ * @lnum_bits: number of bits needed to record LEB number
+ * @nnode_sz: size of on-flash nnode
+ * @pnode_sz: size of on-flash pnode
+ * @ltab_sz: size of on-flash LPT lprops table
+ * @lsave_sz: size of on-flash LPT save table
+ * @pnode_cnt: number of pnodes
+ * @nnode_cnt: number of nnodes
+ * @lpt_hght: height of the LPT
+ * @pnodes_have: number of pnodes in memory
+ *
+ * @lp_mutex: protects lprops table and all the other lprops-related fields
+ * @lpt_lnum: LEB number of the root nnode of the LPT
+ * @lpt_offs: offset of the root nnode of the LPT
+ * @nhead_lnum: LEB number of LPT head
+ * @nhead_offs: offset of LPT head
+ * @lpt_drty_flgs: dirty flags for LPT special nodes e.g. ltab
+ * @dirty_nn_cnt: number of dirty nnodes
+ * @dirty_pn_cnt: number of dirty pnodes
+ * @check_lpt_free: flag that indicates LPT GC may be needed
+ * @lpt_sz: LPT size
+ * @lpt_nod_buf: buffer for an on-flash nnode or pnode
+ * @lpt_buf: buffer of LEB size used by LPT
+ * @nroot: address in memory of the root nnode of the LPT
+ * @lpt_cnext: next LPT node to commit
+ * @lpt_heap: array of heaps of categorized lprops
+ * @dirty_idx: a (reverse sorted) copy of the LPROPS_DIRTY_IDX heap as at
+ * previous commit start
+ * @uncat_list: list of un-categorized LEBs
+ * @empty_list: list of empty LEBs
+ * @freeable_list: list of freeable non-index LEBs (free + dirty == @leb_size)
+ * @frdi_idx_list: list of freeable index LEBs (free + dirty == @leb_size)
+ * @freeable_cnt: number of freeable LEBs in @freeable_list
+ * @in_a_category_cnt: count of lprops which are in a certain category, which
+ * basically meants that they were loaded from the flash
+ *
+ * @ltab_lnum: LEB number of LPT's own lprops table
+ * @ltab_offs: offset of LPT's own lprops table
+ * @ltab: LPT's own lprops table
+ * @ltab_cmt: LPT's own lprops table (commit copy)
+ * @lsave_cnt: number of LEB numbers in LPT's save table
+ * @lsave_lnum: LEB number of LPT's save table
+ * @lsave_offs: offset of LPT's save table
+ * @lsave: LPT's save table
+ * @lscan_lnum: LEB number of last LPT scan
+ *
+ * @rp_size: size of the reserved pool in bytes
+ * @report_rp_size: size of the reserved pool reported to user-space
+ * @rp_uid: reserved pool user ID
+ * @rp_gid: reserved pool group ID
+ *
+ * @empty: %1 if the UBI device is empty
+ * @need_recovery: %1 if the file-system needs recovery
+ * @replaying: %1 during journal replay
+ * @mounting: %1 while mounting
+ * @probing: %1 while attempting to mount if MS_SILENT mount flag is set
+ * @remounting_rw: %1 while re-mounting from R/O mode to R/W mode
+ * @replay_list: temporary list used during journal replay
+ * @replay_buds: list of buds to replay
+ * @cs_sqnum: sequence number of first node in the log (commit start node)
+ * @replay_sqnum: sequence number of node currently being replayed
+ * @unclean_leb_list: LEBs to recover when re-mounting R/O mounted FS to R/W
+ * mode
+ * @rcvrd_mst_node: recovered master node to write when re-mounting R/O mounted
+ * FS to R/W mode
+ * @size_tree: inode size information for recovery
+ * @mount_opts: UBIFS-specific mount options
+ *
+ * @dbg: debugging-related information
+ */
+struct ubifs_info {
+ struct super_block *vfs_sb;
+#ifndef __UBOOT__
+ struct backing_dev_info bdi;
+#endif
+
+ ino_t highest_inum;
+ unsigned long long max_sqnum;
+ unsigned long long cmt_no;
+ spinlock_t cnt_lock;
+ int fmt_version;
+ int ro_compat_version;
+ unsigned char uuid[16];
+
+ int lhead_lnum;
+ int lhead_offs;
+ int ltail_lnum;
+ struct mutex log_mutex;
+ int min_log_bytes;
+ long long cmt_bud_bytes;
+
+ struct rb_root buds;
+ long long bud_bytes;
+ spinlock_t buds_lock;
+ int jhead_cnt;
+ struct ubifs_jhead *jheads;
+ long long max_bud_bytes;
+ long long bg_bud_bytes;
+ struct list_head old_buds;
+ int max_bud_cnt;
+
+ struct rw_semaphore commit_sem;
+ int cmt_state;
+ spinlock_t cs_lock;
+ wait_queue_head_t cmt_wq;
+
+ unsigned int big_lpt:1;
+ unsigned int space_fixup:1;
+ unsigned int no_chk_data_crc:1;
+ unsigned int bulk_read:1;
+ unsigned int default_compr:2;
+ unsigned int rw_incompat:1;
+
+ struct mutex tnc_mutex;
+ struct ubifs_zbranch zroot;
+ struct ubifs_znode *cnext;
+ struct ubifs_znode *enext;
+ int *gap_lebs;
+ void *cbuf;
+ void *ileb_buf;
+ int ileb_len;
+ int ihead_lnum;
+ int ihead_offs;
+ int *ilebs;
+ int ileb_cnt;
+ int ileb_nxt;
+ struct rb_root old_idx;
+ int *bottom_up_buf;
+
+ struct ubifs_mst_node *mst_node;
+ int mst_offs;
+
+ int max_bu_buf_len;
+ struct mutex bu_mutex;
+ struct bu_info bu;
+
+ struct mutex write_reserve_mutex;
+ void *write_reserve_buf;
+
+ int log_lebs;
+ long long log_bytes;
+ int log_last;
+ int lpt_lebs;
+ int lpt_first;
+ int lpt_last;
+ int orph_lebs;
+ int orph_first;
+ int orph_last;
+ int main_lebs;
+ int main_first;
+ long long main_bytes;
+
+ uint8_t key_hash_type;
+ uint32_t (*key_hash)(const char *str, int len);
+ int key_fmt;
+ int key_len;
+ int fanout;
+
+ int min_io_size;
+ int min_io_shift;
+ int max_write_size;
+ int max_write_shift;
+ int leb_size;
+ int leb_start;
+ int half_leb_size;
+ int idx_leb_size;
+ int leb_cnt;
+ int max_leb_cnt;
+ int old_leb_cnt;
+ unsigned int ro_media:1;
+ unsigned int ro_mount:1;
+ unsigned int ro_error:1;
+
+ atomic_long_t dirty_pg_cnt;
+ atomic_long_t dirty_zn_cnt;
+ atomic_long_t clean_zn_cnt;
+
+ spinlock_t space_lock;
+ struct ubifs_lp_stats lst;
+ struct ubifs_budg_info bi;
+ unsigned long long calc_idx_sz;
+
+ int ref_node_alsz;
+ int mst_node_alsz;
+ int min_idx_node_sz;
+ int max_idx_node_sz;
+ long long max_inode_sz;
+ int max_znode_sz;
+
+ int leb_overhead;
+ int dead_wm;
+ int dark_wm;
+ int block_cnt;
+
+ struct ubifs_node_range ranges[UBIFS_NODE_TYPES_CNT];
+ struct ubi_volume_desc *ubi;
+ struct ubi_device_info di;
+ struct ubi_volume_info vi;
+
+ struct rb_root orph_tree;
+ struct list_head orph_list;
+ struct list_head orph_new;
+ struct ubifs_orphan *orph_cnext;
+ struct ubifs_orphan *orph_dnext;
+ spinlock_t orphan_lock;
+ void *orph_buf;
+ int new_orphans;
+ int cmt_orphans;
+ int tot_orphans;
+ int max_orphans;
+ int ohead_lnum;
+ int ohead_offs;
+ int no_orphs;
+
+ struct task_struct *bgt;
+ char bgt_name[sizeof(BGT_NAME_PATTERN) + 9];
+ int need_bgt;
+ int need_wbuf_sync;
+
+ int gc_lnum;
+ void *sbuf;
+ struct list_head idx_gc;
+ int idx_gc_cnt;
+ int gc_seq;
+ int gced_lnum;
+
+ struct list_head infos_list;
+ struct mutex umount_mutex;
+ unsigned int shrinker_run_no;
+
+ int space_bits;
+ int lpt_lnum_bits;
+ int lpt_offs_bits;
+ int lpt_spc_bits;
+ int pcnt_bits;
+ int lnum_bits;
+ int nnode_sz;
+ int pnode_sz;
+ int ltab_sz;
+ int lsave_sz;
+ int pnode_cnt;
+ int nnode_cnt;
+ int lpt_hght;
+ int pnodes_have;
+
+ struct mutex lp_mutex;
+ int lpt_lnum;
+ int lpt_offs;
+ int nhead_lnum;
+ int nhead_offs;
+ int lpt_drty_flgs;
+ int dirty_nn_cnt;
+ int dirty_pn_cnt;
+ int check_lpt_free;
+ long long lpt_sz;
+ void *lpt_nod_buf;
+ void *lpt_buf;
+ struct ubifs_nnode *nroot;
+ struct ubifs_cnode *lpt_cnext;
+ struct ubifs_lpt_heap lpt_heap[LPROPS_HEAP_CNT];
+ struct ubifs_lpt_heap dirty_idx;
+ struct list_head uncat_list;
+ struct list_head empty_list;
+ struct list_head freeable_list;
+ struct list_head frdi_idx_list;
+ int freeable_cnt;
+ int in_a_category_cnt;
+
+ int ltab_lnum;
+ int ltab_offs;
+ struct ubifs_lpt_lprops *ltab;
+ struct ubifs_lpt_lprops *ltab_cmt;
+ int lsave_cnt;
+ int lsave_lnum;
+ int lsave_offs;
+ int *lsave;
+ int lscan_lnum;
+
+ long long rp_size;
+ long long report_rp_size;
+ kuid_t rp_uid;
+ kgid_t rp_gid;
+
+ /* The below fields are used only during mounting and re-mounting */
+ unsigned int empty:1;
+ unsigned int need_recovery:1;
+ unsigned int replaying:1;
+ unsigned int mounting:1;
+ unsigned int remounting_rw:1;
+ unsigned int probing:1;
+ struct list_head replay_list;
+ struct list_head replay_buds;
+ unsigned long long cs_sqnum;
+ unsigned long long replay_sqnum;
+ struct list_head unclean_leb_list;
+ struct ubifs_mst_node *rcvrd_mst_node;
+ struct rb_root size_tree;
+ struct ubifs_mount_opts mount_opts;
+
+#ifndef __UBOOT__
+ struct ubifs_debug_info *dbg;
+#endif
+};
+
+extern struct list_head ubifs_infos;
+extern spinlock_t ubifs_infos_lock;
+extern atomic_long_t ubifs_clean_zn_cnt;
+extern struct kmem_cache *ubifs_inode_slab;
+extern const struct super_operations ubifs_super_operations;
+extern const struct xattr_handler *ubifs_xattr_handlers[];
+extern const struct address_space_operations ubifs_file_address_operations;
+extern const struct file_operations ubifs_file_operations;
+extern const struct inode_operations ubifs_file_inode_operations;
+extern const struct file_operations ubifs_dir_operations;
+extern const struct inode_operations ubifs_dir_inode_operations;
+extern const struct inode_operations ubifs_symlink_inode_operations;
+extern struct backing_dev_info ubifs_backing_dev_info;
+extern struct ubifs_compressor *ubifs_compressors[UBIFS_COMPR_TYPES_CNT];
+
+/* io.c */
+void ubifs_ro_mode(struct ubifs_info *c, int err);
+int ubifs_leb_read(const struct ubifs_info *c, int lnum, void *buf, int offs,
+ int len, int even_ebadmsg);
+int ubifs_leb_write(struct ubifs_info *c, int lnum, const void *buf, int offs,
+ int len);
+int ubifs_leb_change(struct ubifs_info *c, int lnum, const void *buf, int len);
+int ubifs_leb_unmap(struct ubifs_info *c, int lnum);
+int ubifs_leb_map(struct ubifs_info *c, int lnum);
+int ubifs_is_mapped(const struct ubifs_info *c, int lnum);
+int ubifs_wbuf_write_nolock(struct ubifs_wbuf *wbuf, void *buf, int len);
+int ubifs_wbuf_seek_nolock(struct ubifs_wbuf *wbuf, int lnum, int offs);
+int ubifs_wbuf_init(struct ubifs_info *c, struct ubifs_wbuf *wbuf);
+int ubifs_read_node(const struct ubifs_info *c, void *buf, int type, int len,
+ int lnum, int offs);
+int ubifs_read_node_wbuf(struct ubifs_wbuf *wbuf, void *buf, int type, int len,
+ int lnum, int offs);
+int ubifs_write_node(struct ubifs_info *c, void *node, int len, int lnum,
+ int offs);
+int ubifs_check_node(const struct ubifs_info *c, const void *buf, int lnum,
+ int offs, int quiet, int must_chk_crc);
+void ubifs_prepare_node(struct ubifs_info *c, void *buf, int len, int pad);
+void ubifs_prep_grp_node(struct ubifs_info *c, void *node, int len, int last);
+int ubifs_io_init(struct ubifs_info *c);
+void ubifs_pad(const struct ubifs_info *c, void *buf, int pad);
+int ubifs_wbuf_sync_nolock(struct ubifs_wbuf *wbuf);
+int ubifs_bg_wbufs_sync(struct ubifs_info *c);
+void ubifs_wbuf_add_ino_nolock(struct ubifs_wbuf *wbuf, ino_t inum);
+int ubifs_sync_wbufs_by_inode(struct ubifs_info *c, struct inode *inode);
+
+/* scan.c */
+struct ubifs_scan_leb *ubifs_scan(const struct ubifs_info *c, int lnum,
+ int offs, void *sbuf, int quiet);
+void ubifs_scan_destroy(struct ubifs_scan_leb *sleb);
+int ubifs_scan_a_node(const struct ubifs_info *c, void *buf, int len, int lnum,
+ int offs, int quiet);
+struct ubifs_scan_leb *ubifs_start_scan(const struct ubifs_info *c, int lnum,
+ int offs, void *sbuf);
+void ubifs_end_scan(const struct ubifs_info *c, struct ubifs_scan_leb *sleb,
+ int lnum, int offs);
+int ubifs_add_snod(const struct ubifs_info *c, struct ubifs_scan_leb *sleb,
+ void *buf, int offs);
+void ubifs_scanned_corruption(const struct ubifs_info *c, int lnum, int offs,
+ void *buf);
+
+/* log.c */
+void ubifs_add_bud(struct ubifs_info *c, struct ubifs_bud *bud);
+void ubifs_create_buds_lists(struct ubifs_info *c);
+int ubifs_add_bud_to_log(struct ubifs_info *c, int jhead, int lnum, int offs);
+struct ubifs_bud *ubifs_search_bud(struct ubifs_info *c, int lnum);
+struct ubifs_wbuf *ubifs_get_wbuf(struct ubifs_info *c, int lnum);
+int ubifs_log_start_commit(struct ubifs_info *c, int *ltail_lnum);
+int ubifs_log_end_commit(struct ubifs_info *c, int new_ltail_lnum);
+int ubifs_log_post_commit(struct ubifs_info *c, int old_ltail_lnum);
+int ubifs_consolidate_log(struct ubifs_info *c);
+
+/* journal.c */
+int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
+ const struct qstr *nm, const struct inode *inode,
+ int deletion, int xent);
+int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
+ const union ubifs_key *key, const void *buf, int len);
+int ubifs_jnl_write_inode(struct ubifs_info *c, const struct inode *inode);
+int ubifs_jnl_delete_inode(struct ubifs_info *c, const struct inode *inode);
+int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
+ const struct dentry *old_dentry,
+ const struct inode *new_dir,
+ const struct dentry *new_dentry, int sync);
+int ubifs_jnl_truncate(struct ubifs_info *c, const struct inode *inode,
+ loff_t old_size, loff_t new_size);
+int ubifs_jnl_delete_xattr(struct ubifs_info *c, const struct inode *host,
+ const struct inode *inode, const struct qstr *nm);
+int ubifs_jnl_change_xattr(struct ubifs_info *c, const struct inode *inode1,
+ const struct inode *inode2);
+
+/* budget.c */
+int ubifs_budget_space(struct ubifs_info *c, struct ubifs_budget_req *req);
+void ubifs_release_budget(struct ubifs_info *c, struct ubifs_budget_req *req);
+void ubifs_release_dirty_inode_budget(struct ubifs_info *c,
+ struct ubifs_inode *ui);
+int ubifs_budget_inode_op(struct ubifs_info *c, struct inode *inode,
+ struct ubifs_budget_req *req);
+void ubifs_release_ino_dirty(struct ubifs_info *c, struct inode *inode,
+ struct ubifs_budget_req *req);
+void ubifs_cancel_ino_op(struct ubifs_info *c, struct inode *inode,
+ struct ubifs_budget_req *req);
+long long ubifs_get_free_space(struct ubifs_info *c);
+long long ubifs_get_free_space_nolock(struct ubifs_info *c);
+int ubifs_calc_min_idx_lebs(struct ubifs_info *c);
+void ubifs_convert_page_budget(struct ubifs_info *c);
+long long ubifs_reported_space(const struct ubifs_info *c, long long free);
+long long ubifs_calc_available(const struct ubifs_info *c, int min_idx_lebs);
+
+/* find.c */
+int ubifs_find_free_space(struct ubifs_info *c, int min_space, int *offs,
+ int squeeze);
+int ubifs_find_free_leb_for_idx(struct ubifs_info *c);
+int ubifs_find_dirty_leb(struct ubifs_info *c, struct ubifs_lprops *ret_lp,
+ int min_space, int pick_free);
+int ubifs_find_dirty_idx_leb(struct ubifs_info *c);
+int ubifs_save_dirty_idx_lnums(struct ubifs_info *c);
+
+/* tnc.c */
+int ubifs_lookup_level0(struct ubifs_info *c, const union ubifs_key *key,
+ struct ubifs_znode **zn, int *n);
+int ubifs_tnc_lookup_nm(struct ubifs_info *c, const union ubifs_key *key,
+ void *node, const struct qstr *nm);
+int ubifs_tnc_locate(struct ubifs_info *c, const union ubifs_key *key,
+ void *node, int *lnum, int *offs);
+int ubifs_tnc_add(struct ubifs_info *c, const union ubifs_key *key, int lnum,
+ int offs, int len);
+int ubifs_tnc_replace(struct ubifs_info *c, const union ubifs_key *key,
+ int old_lnum, int old_offs, int lnum, int offs, int len);
+int ubifs_tnc_add_nm(struct ubifs_info *c, const union ubifs_key *key,
+ int lnum, int offs, int len, const struct qstr *nm);
+int ubifs_tnc_remove(struct ubifs_info *c, const union ubifs_key *key);
+int ubifs_tnc_remove_nm(struct ubifs_info *c, const union ubifs_key *key,
+ const struct qstr *nm);
+int ubifs_tnc_remove_range(struct ubifs_info *c, union ubifs_key *from_key,
+ union ubifs_key *to_key);
+int ubifs_tnc_remove_ino(struct ubifs_info *c, ino_t inum);
+struct ubifs_dent_node *ubifs_tnc_next_ent(struct ubifs_info *c,
+ union ubifs_key *key,
+ const struct qstr *nm);
+void ubifs_tnc_close(struct ubifs_info *c);
+int ubifs_tnc_has_node(struct ubifs_info *c, union ubifs_key *key, int level,
+ int lnum, int offs, int is_idx);
+int ubifs_dirty_idx_node(struct ubifs_info *c, union ubifs_key *key, int level,
+ int lnum, int offs);
+/* Shared by tnc.c for tnc_commit.c */
+void destroy_old_idx(struct ubifs_info *c);
+int is_idx_node_in_tnc(struct ubifs_info *c, union ubifs_key *key, int level,
+ int lnum, int offs);
+int insert_old_idx_znode(struct ubifs_info *c, struct ubifs_znode *znode);
+int ubifs_tnc_get_bu_keys(struct ubifs_info *c, struct bu_info *bu);
+int ubifs_tnc_bulk_read(struct ubifs_info *c, struct bu_info *bu);
+
+/* tnc_misc.c */
+struct ubifs_znode *ubifs_tnc_levelorder_next(struct ubifs_znode *zr,
+ struct ubifs_znode *znode);
+int ubifs_search_zbranch(const struct ubifs_info *c,
+ const struct ubifs_znode *znode,
+ const union ubifs_key *key, int *n);
+struct ubifs_znode *ubifs_tnc_postorder_first(struct ubifs_znode *znode);
+struct ubifs_znode *ubifs_tnc_postorder_next(struct ubifs_znode *znode);
+long ubifs_destroy_tnc_subtree(struct ubifs_znode *zr);
+struct ubifs_znode *ubifs_load_znode(struct ubifs_info *c,
+ struct ubifs_zbranch *zbr,
+ struct ubifs_znode *parent, int iip);
+int ubifs_tnc_read_node(struct ubifs_info *c, struct ubifs_zbranch *zbr,
+ void *node);
+
+/* tnc_commit.c */
+int ubifs_tnc_start_commit(struct ubifs_info *c, struct ubifs_zbranch *zroot);
+int ubifs_tnc_end_commit(struct ubifs_info *c);
+
+#ifndef __UBOOT__
+/* shrinker.c */
+unsigned long ubifs_shrink_scan(struct shrinker *shrink,
+ struct shrink_control *sc);
+unsigned long ubifs_shrink_count(struct shrinker *shrink,
+ struct shrink_control *sc);
+#endif
+
+/* commit.c */
+int ubifs_bg_thread(void *info);
+void ubifs_commit_required(struct ubifs_info *c);
+void ubifs_request_bg_commit(struct ubifs_info *c);
+int ubifs_run_commit(struct ubifs_info *c);
+void ubifs_recovery_commit(struct ubifs_info *c);
+int ubifs_gc_should_commit(struct ubifs_info *c);
+void ubifs_wait_for_commit(struct ubifs_info *c);
+
+/* master.c */
+int ubifs_read_master(struct ubifs_info *c);
+int ubifs_write_master(struct ubifs_info *c);
+
+/* sb.c */
+int ubifs_read_superblock(struct ubifs_info *c);
+struct ubifs_sb_node *ubifs_read_sb_node(struct ubifs_info *c);
+int ubifs_write_sb_node(struct ubifs_info *c, struct ubifs_sb_node *sup);
+int ubifs_fixup_free_space(struct ubifs_info *c);
+
+/* replay.c */
+int ubifs_validate_entry(struct ubifs_info *c,
+ const struct ubifs_dent_node *dent);
+int ubifs_replay_journal(struct ubifs_info *c);
+
+/* gc.c */
+int ubifs_garbage_collect(struct ubifs_info *c, int anyway);
+int ubifs_gc_start_commit(struct ubifs_info *c);
+int ubifs_gc_end_commit(struct ubifs_info *c);
+void ubifs_destroy_idx_gc(struct ubifs_info *c);
+int ubifs_get_idx_gc_leb(struct ubifs_info *c);
+int ubifs_garbage_collect_leb(struct ubifs_info *c, struct ubifs_lprops *lp);
+
+/* orphan.c */
+int ubifs_add_orphan(struct ubifs_info *c, ino_t inum);
+void ubifs_delete_orphan(struct ubifs_info *c, ino_t inum);
+int ubifs_orphan_start_commit(struct ubifs_info *c);
+int ubifs_orphan_end_commit(struct ubifs_info *c);
+int ubifs_mount_orphans(struct ubifs_info *c, int unclean, int read_only);
+int ubifs_clear_orphans(struct ubifs_info *c);
+
+/* lpt.c */
+int ubifs_calc_lpt_geom(struct ubifs_info *c);
+int ubifs_create_dflt_lpt(struct ubifs_info *c, int *main_lebs, int lpt_first,
+ int *lpt_lebs, int *big_lpt);
+int ubifs_lpt_init(struct ubifs_info *c, int rd, int wr);
+struct ubifs_lprops *ubifs_lpt_lookup(struct ubifs_info *c, int lnum);
+struct ubifs_lprops *ubifs_lpt_lookup_dirty(struct ubifs_info *c, int lnum);
+int ubifs_lpt_scan_nolock(struct ubifs_info *c, int start_lnum, int end_lnum,
+ ubifs_lpt_scan_callback scan_cb, void *data);
+
+/* Shared by lpt.c for lpt_commit.c */
+void ubifs_pack_lsave(struct ubifs_info *c, void *buf, int *lsave);
+void ubifs_pack_ltab(struct ubifs_info *c, void *buf,
+ struct ubifs_lpt_lprops *ltab);
+void ubifs_pack_pnode(struct ubifs_info *c, void *buf,
+ struct ubifs_pnode *pnode);
+void ubifs_pack_nnode(struct ubifs_info *c, void *buf,
+ struct ubifs_nnode *nnode);
+struct ubifs_pnode *ubifs_get_pnode(struct ubifs_info *c,
+ struct ubifs_nnode *parent, int iip);
+struct ubifs_nnode *ubifs_get_nnode(struct ubifs_info *c,
+ struct ubifs_nnode *parent, int iip);
+int ubifs_read_nnode(struct ubifs_info *c, struct ubifs_nnode *parent, int iip);
+void ubifs_add_lpt_dirt(struct ubifs_info *c, int lnum, int dirty);
+void ubifs_add_nnode_dirt(struct ubifs_info *c, struct ubifs_nnode *nnode);
+uint32_t ubifs_unpack_bits(uint8_t **addr, int *pos, int nrbits);
+struct ubifs_nnode *ubifs_first_nnode(struct ubifs_info *c, int *hght);
+/* Needed only in debugging code in lpt_commit.c */
+int ubifs_unpack_nnode(const struct ubifs_info *c, void *buf,
+ struct ubifs_nnode *nnode);
+
+/* lpt_commit.c */
+int ubifs_lpt_start_commit(struct ubifs_info *c);
+int ubifs_lpt_end_commit(struct ubifs_info *c);
+int ubifs_lpt_post_commit(struct ubifs_info *c);
+void ubifs_lpt_free(struct ubifs_info *c, int wr_only);
+
+/* lprops.c */
+const struct ubifs_lprops *ubifs_change_lp(struct ubifs_info *c,
+ const struct ubifs_lprops *lp,
+ int free, int dirty, int flags,
+ int idx_gc_cnt);
+void ubifs_get_lp_stats(struct ubifs_info *c, struct ubifs_lp_stats *lst);
+void ubifs_add_to_cat(struct ubifs_info *c, struct ubifs_lprops *lprops,
+ int cat);
+void ubifs_replace_cat(struct ubifs_info *c, struct ubifs_lprops *old_lprops,
+ struct ubifs_lprops *new_lprops);
+void ubifs_ensure_cat(struct ubifs_info *c, struct ubifs_lprops *lprops);
+int ubifs_categorize_lprops(const struct ubifs_info *c,
+ const struct ubifs_lprops *lprops);
+int ubifs_change_one_lp(struct ubifs_info *c, int lnum, int free, int dirty,
+ int flags_set, int flags_clean, int idx_gc_cnt);
+int ubifs_update_one_lp(struct ubifs_info *c, int lnum, int free, int dirty,
+ int flags_set, int flags_clean);
+int ubifs_read_one_lp(struct ubifs_info *c, int lnum, struct ubifs_lprops *lp);
+const struct ubifs_lprops *ubifs_fast_find_free(struct ubifs_info *c);
+const struct ubifs_lprops *ubifs_fast_find_empty(struct ubifs_info *c);
+const struct ubifs_lprops *ubifs_fast_find_freeable(struct ubifs_info *c);
+const struct ubifs_lprops *ubifs_fast_find_frdi_idx(struct ubifs_info *c);
+int ubifs_calc_dark(const struct ubifs_info *c, int spc);
+
+/* file.c */
+int ubifs_fsync(struct file *file, loff_t start, loff_t end, int datasync);
+int ubifs_setattr(struct dentry *dentry, struct iattr *attr);
+
+/* dir.c */
+struct inode *ubifs_new_inode(struct ubifs_info *c, const struct inode *dir,
+ umode_t mode);
+int ubifs_getattr(struct vfsmount *mnt, struct dentry *dentry,
+ struct kstat *stat);
+
+/* xattr.c */
+int ubifs_setxattr(struct dentry *dentry, const char *name,
+ const void *value, size_t size, int flags);
+ssize_t ubifs_getxattr(struct dentry *dentry, const char *name, void *buf,
+ size_t size);
+ssize_t ubifs_listxattr(struct dentry *dentry, char *buffer, size_t size);
+int ubifs_removexattr(struct dentry *dentry, const char *name);
+int ubifs_init_security(struct inode *dentry, struct inode *inode,
+ const struct qstr *qstr);
+
+/* super.c */
+struct inode *ubifs_iget(struct super_block *sb, unsigned long inum);
+int ubifs_iput(struct inode *inode);
+
+/* recovery.c */
+int ubifs_recover_master_node(struct ubifs_info *c);
+int ubifs_write_rcvrd_mst_node(struct ubifs_info *c);
+struct ubifs_scan_leb *ubifs_recover_leb(struct ubifs_info *c, int lnum,
+ int offs, void *sbuf, int jhead);
+struct ubifs_scan_leb *ubifs_recover_log_leb(struct ubifs_info *c, int lnum,
+ int offs, void *sbuf);
+int ubifs_recover_inl_heads(struct ubifs_info *c, void *sbuf);
+int ubifs_clean_lebs(struct ubifs_info *c, void *sbuf);
+int ubifs_rcvry_gc_commit(struct ubifs_info *c);
+int ubifs_recover_size_accum(struct ubifs_info *c, union ubifs_key *key,
+ int deletion, loff_t new_size);
+int ubifs_recover_size(struct ubifs_info *c);
+void ubifs_destroy_size_tree(struct ubifs_info *c);
+
+/* ioctl.c */
+long ubifs_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
+void ubifs_set_inode_flags(struct inode *inode);
+#ifdef CONFIG_COMPAT
+long ubifs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
+#endif
+
+/* compressor.c */
+int __init ubifs_compressors_init(void);
+void ubifs_compressors_exit(void);
+void ubifs_compress(const struct ubifs_info *c, const void *in_buf, int in_len,
+ void *out_buf, int *out_len, int *compr_type);
+int ubifs_decompress(const struct ubifs_info *c, const void *buf, int len,
+ void *out, int *out_len, int compr_type);
+
+#include "debug.h"
+#include "misc.h"
+#include "key.h"
+
+#ifdef __UBOOT__
+void ubifs_umount(struct ubifs_info *c);
+#endif
+#endif /* !__UBIFS_H__ */
diff --git a/roms/u-boot/fs/yaffs2/Kconfig b/roms/u-boot/fs/yaffs2/Kconfig
new file mode 100644
index 000000000..45ffdf6c2
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/Kconfig
@@ -0,0 +1,7 @@
+config YAFFS2
+ bool "YAFFS2 filesystem support"
+ help
+ This provides access to YAFFS2 filesystems. Yet Another Flash
+ Filesystem 2 is a filesystem designed specifically for NAND flash.
+ It incorporates bad-block management and ensures that device
+ writes are sequential regardless of filesystem activity.
diff --git a/roms/u-boot/fs/yaffs2/Makefile b/roms/u-boot/fs/yaffs2/Makefile
new file mode 100644
index 000000000..3c1bb4492
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/Makefile
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: GPL-2.0
+# Makefile for YAFFS direct test
+# YAFFS: Yet another Flash File System. A NAND-flash specific file system.
+#
+# Copyright (C) 2003 Aleph One Ltd.
+#
+#
+# Created by Charles Manning <charles@aleph1.co.uk>
+#
+#
+
+obj-y := \
+ yaffs_allocator.o yaffs_attribs.o yaffs_bitmap.o yaffs_uboot_glue.o\
+ yaffs_checkptrw.o yaffs_ecc.o yaffs_error.o \
+ yaffsfs.o yaffs_guts.o yaffs_nameval.o yaffs_nand.o\
+ yaffs_packedtags1.o yaffs_packedtags2.o yaffs_qsort.o \
+ yaffs_summary.o yaffs_tagscompat.o yaffs_verify.o yaffs_yaffs1.o \
+ yaffs_yaffs2.o yaffs_mtdif.o yaffs_mtdif2.o
+
+ccflags-y = -DCONFIG_YAFFS_DIRECT -DCONFIG_YAFFS_SHORT_NAMES_IN_RAM \
+ -DCONFIG_YAFFS_YAFFS2 -DNO_Y_INLINE \
+ -DCONFIG_YAFFS_PROVIDE_DEFS -DCONFIG_YAFFSFS_PROVIDE_VALUES
diff --git a/roms/u-boot/fs/yaffs2/yaffs_allocator.c b/roms/u-boot/fs/yaffs2/yaffs_allocator.c
new file mode 100644
index 000000000..961dc22ef
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_allocator.c
@@ -0,0 +1,357 @@
+/*
+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "yaffs_allocator.h"
+#include "yaffs_guts.h"
+#include "yaffs_trace.h"
+#include "yportenv.h"
+#include <dm/devres.h>
+
+/*
+ * Each entry in yaffs_tnode_list and yaffs_obj_list hold blocks
+ * of approx 100 objects that are themn allocated singly.
+ * This is basically a simplified slab allocator.
+ *
+ * We don't use the Linux slab allocator because slab does not allow
+ * us to dump all the objects in one hit when we do a umount and tear
+ * down all the tnodes and objects. slab requires that we first free
+ * the individual objects.
+ *
+ * Once yaffs has been mainlined I shall try to motivate for a change
+ * to slab to provide the extra features we need here.
+ */
+
+struct yaffs_tnode_list {
+ struct yaffs_tnode_list *next;
+ struct yaffs_tnode *tnodes;
+};
+
+struct yaffs_obj_list {
+ struct yaffs_obj_list *next;
+ struct yaffs_obj *objects;
+};
+
+struct yaffs_allocator {
+ int n_tnodes_created;
+ struct yaffs_tnode *free_tnodes;
+ int n_free_tnodes;
+ struct yaffs_tnode_list *alloc_tnode_list;
+
+ int n_obj_created;
+ struct list_head free_objs;
+ int n_free_objects;
+
+ struct yaffs_obj_list *allocated_obj_list;
+};
+
+static void yaffs_deinit_raw_tnodes(struct yaffs_dev *dev)
+{
+ struct yaffs_allocator *allocator =
+ (struct yaffs_allocator *)dev->allocator;
+ struct yaffs_tnode_list *tmp;
+
+ if (!allocator) {
+ BUG();
+ return;
+ }
+
+ while (allocator->alloc_tnode_list) {
+ tmp = allocator->alloc_tnode_list->next;
+
+ kfree(allocator->alloc_tnode_list->tnodes);
+ kfree(allocator->alloc_tnode_list);
+ allocator->alloc_tnode_list = tmp;
+ }
+
+ allocator->free_tnodes = NULL;
+ allocator->n_free_tnodes = 0;
+ allocator->n_tnodes_created = 0;
+}
+
+static void yaffs_init_raw_tnodes(struct yaffs_dev *dev)
+{
+ struct yaffs_allocator *allocator = dev->allocator;
+
+ if (!allocator) {
+ BUG();
+ return;
+ }
+
+ allocator->alloc_tnode_list = NULL;
+ allocator->free_tnodes = NULL;
+ allocator->n_free_tnodes = 0;
+ allocator->n_tnodes_created = 0;
+}
+
+static int yaffs_create_tnodes(struct yaffs_dev *dev, int n_tnodes)
+{
+ struct yaffs_allocator *allocator =
+ (struct yaffs_allocator *)dev->allocator;
+ int i;
+ struct yaffs_tnode *new_tnodes;
+ u8 *mem;
+ struct yaffs_tnode *curr;
+ struct yaffs_tnode *next;
+ struct yaffs_tnode_list *tnl;
+
+ if (!allocator) {
+ BUG();
+ return YAFFS_FAIL;
+ }
+
+ if (n_tnodes < 1)
+ return YAFFS_OK;
+
+ /* make these things */
+ new_tnodes = kmalloc(n_tnodes * dev->tnode_size, GFP_NOFS);
+ mem = (u8 *) new_tnodes;
+
+ if (!new_tnodes) {
+ yaffs_trace(YAFFS_TRACE_ERROR,
+ "yaffs: Could not allocate Tnodes");
+ return YAFFS_FAIL;
+ }
+
+ /* New hookup for wide tnodes */
+ for (i = 0; i < n_tnodes - 1; i++) {
+ curr = (struct yaffs_tnode *)&mem[i * dev->tnode_size];
+ next = (struct yaffs_tnode *)&mem[(i + 1) * dev->tnode_size];
+ curr->internal[0] = next;
+ }
+
+ curr = (struct yaffs_tnode *)&mem[(n_tnodes - 1) * dev->tnode_size];
+ curr->internal[0] = allocator->free_tnodes;
+ allocator->free_tnodes = (struct yaffs_tnode *)mem;
+
+ allocator->n_free_tnodes += n_tnodes;
+ allocator->n_tnodes_created += n_tnodes;
+
+ /* Now add this bunch of tnodes to a list for freeing up.
+ * NB If we can't add this to the management list it isn't fatal
+ * but it just means we can't free this bunch of tnodes later.
+ */
+ tnl = kmalloc(sizeof(struct yaffs_tnode_list), GFP_NOFS);
+ if (!tnl) {
+ yaffs_trace(YAFFS_TRACE_ERROR,
+ "Could not add tnodes to management list");
+ return YAFFS_FAIL;
+ } else {
+ tnl->tnodes = new_tnodes;
+ tnl->next = allocator->alloc_tnode_list;
+ allocator->alloc_tnode_list = tnl;
+ }
+
+ yaffs_trace(YAFFS_TRACE_ALLOCATE, "Tnodes added");
+
+ return YAFFS_OK;
+}
+
+struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev)
+{
+ struct yaffs_allocator *allocator =
+ (struct yaffs_allocator *)dev->allocator;
+ struct yaffs_tnode *tn = NULL;
+
+ if (!allocator) {
+ BUG();
+ return NULL;
+ }
+
+ /* If there are none left make more */
+ if (!allocator->free_tnodes)
+ yaffs_create_tnodes(dev, YAFFS_ALLOCATION_NTNODES);
+
+ if (allocator->free_tnodes) {
+ tn = allocator->free_tnodes;
+ allocator->free_tnodes = allocator->free_tnodes->internal[0];
+ allocator->n_free_tnodes--;
+ }
+
+ return tn;
+}
+
+/* FreeTnode frees up a tnode and puts it back on the free list */
+void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn)
+{
+ struct yaffs_allocator *allocator = dev->allocator;
+
+ if (!allocator) {
+ BUG();
+ return;
+ }
+
+ if (tn) {
+ tn->internal[0] = allocator->free_tnodes;
+ allocator->free_tnodes = tn;
+ allocator->n_free_tnodes++;
+ }
+ dev->checkpoint_blocks_required = 0; /* force recalculation */
+}
+
+/*--------------- yaffs_obj alloaction ------------------------
+ *
+ * Free yaffs_objs are stored in a list using obj->siblings.
+ * The blocks of allocated objects are stored in a linked list.
+ */
+
+static void yaffs_init_raw_objs(struct yaffs_dev *dev)
+{
+ struct yaffs_allocator *allocator = dev->allocator;
+
+ if (!allocator) {
+ BUG();
+ return;
+ }
+
+ allocator->allocated_obj_list = NULL;
+ INIT_LIST_HEAD(&allocator->free_objs);
+ allocator->n_free_objects = 0;
+}
+
+static void yaffs_deinit_raw_objs(struct yaffs_dev *dev)
+{
+ struct yaffs_allocator *allocator = dev->allocator;
+ struct yaffs_obj_list *tmp;
+
+ if (!allocator) {
+ BUG();
+ return;
+ }
+
+ while (allocator->allocated_obj_list) {
+ tmp = allocator->allocated_obj_list->next;
+ kfree(allocator->allocated_obj_list->objects);
+ kfree(allocator->allocated_obj_list);
+ allocator->allocated_obj_list = tmp;
+ }
+
+ INIT_LIST_HEAD(&allocator->free_objs);
+ allocator->n_free_objects = 0;
+ allocator->n_obj_created = 0;
+}
+
+static int yaffs_create_free_objs(struct yaffs_dev *dev, int n_obj)
+{
+ struct yaffs_allocator *allocator = dev->allocator;
+ int i;
+ struct yaffs_obj *new_objs;
+ struct yaffs_obj_list *list;
+
+ if (!allocator) {
+ BUG();
+ return YAFFS_FAIL;
+ }
+
+ if (n_obj < 1)
+ return YAFFS_OK;
+
+ /* make these things */
+ new_objs = kmalloc(n_obj * sizeof(struct yaffs_obj), GFP_NOFS);
+ list = kmalloc(sizeof(struct yaffs_obj_list), GFP_NOFS);
+
+ if (!new_objs || !list) {
+ kfree(new_objs);
+ new_objs = NULL;
+ kfree(list);
+ list = NULL;
+ yaffs_trace(YAFFS_TRACE_ALLOCATE,
+ "Could not allocate more objects");
+ return YAFFS_FAIL;
+ }
+
+ /* Hook them into the free list */
+ for (i = 0; i < n_obj; i++)
+ list_add(&new_objs[i].siblings, &allocator->free_objs);
+
+ allocator->n_free_objects += n_obj;
+ allocator->n_obj_created += n_obj;
+
+ /* Now add this bunch of Objects to a list for freeing up. */
+
+ list->objects = new_objs;
+ list->next = allocator->allocated_obj_list;
+ allocator->allocated_obj_list = list;
+
+ return YAFFS_OK;
+}
+
+struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev)
+{
+ struct yaffs_obj *obj = NULL;
+ struct list_head *lh;
+ struct yaffs_allocator *allocator = dev->allocator;
+
+ if (!allocator) {
+ BUG();
+ return obj;
+ }
+
+ /* If there are none left make more */
+ if (list_empty(&allocator->free_objs))
+ yaffs_create_free_objs(dev, YAFFS_ALLOCATION_NOBJECTS);
+
+ if (!list_empty(&allocator->free_objs)) {
+ lh = allocator->free_objs.next;
+ obj = list_entry(lh, struct yaffs_obj, siblings);
+ list_del_init(lh);
+ allocator->n_free_objects--;
+ }
+
+ return obj;
+}
+
+void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj)
+{
+
+ struct yaffs_allocator *allocator = dev->allocator;
+
+ if (!allocator) {
+ BUG();
+ return;
+ }
+
+ /* Link into the free list. */
+ list_add(&obj->siblings, &allocator->free_objs);
+ allocator->n_free_objects++;
+}
+
+void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev)
+{
+
+ if (!dev->allocator) {
+ BUG();
+ return;
+ }
+
+ yaffs_deinit_raw_tnodes(dev);
+ yaffs_deinit_raw_objs(dev);
+ kfree(dev->allocator);
+ dev->allocator = NULL;
+}
+
+void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev)
+{
+ struct yaffs_allocator *allocator;
+
+ if (dev->allocator) {
+ BUG();
+ return;
+ }
+
+ allocator = kmalloc(sizeof(struct yaffs_allocator), GFP_NOFS);
+ if (allocator) {
+ dev->allocator = allocator;
+ yaffs_init_raw_tnodes(dev);
+ yaffs_init_raw_objs(dev);
+ }
+}
diff --git a/roms/u-boot/fs/yaffs2/yaffs_allocator.h b/roms/u-boot/fs/yaffs2/yaffs_allocator.h
new file mode 100644
index 000000000..a8cc32264
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_allocator.h
@@ -0,0 +1,30 @@
+/*
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ */
+
+#ifndef __YAFFS_ALLOCATOR_H__
+#define __YAFFS_ALLOCATOR_H__
+
+#include "yaffs_guts.h"
+
+void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev);
+void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev);
+
+struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev);
+void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn);
+
+struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev);
+void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj);
+
+#endif
diff --git a/roms/u-boot/fs/yaffs2/yaffs_attribs.c b/roms/u-boot/fs/yaffs2/yaffs_attribs.c
new file mode 100644
index 000000000..69664268e
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_attribs.c
@@ -0,0 +1,152 @@
+/*
+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "yaffs_attribs.h"
+
+
+void yaffs_load_attribs(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh)
+{
+ obj->yst_uid = oh->yst_uid;
+ obj->yst_gid = oh->yst_gid;
+ obj->yst_atime = oh->yst_atime;
+ obj->yst_mtime = oh->yst_mtime;
+ obj->yst_ctime = oh->yst_ctime;
+ obj->yst_rdev = oh->yst_rdev;
+}
+
+
+void yaffs_load_attribs_oh(struct yaffs_obj_hdr *oh, struct yaffs_obj *obj)
+{
+#ifdef CONFIG_YAFFS_WINCE
+ oh->win_atime[0] = obj->win_atime[0];
+ oh->win_ctime[0] = obj->win_ctime[0];
+ oh->win_mtime[0] = obj->win_mtime[0];
+ oh->win_atime[1] = obj->win_atime[1];
+ oh->win_ctime[1] = obj->win_ctime[1];
+ oh->win_mtime[1] = obj->win_mtime[1];
+#else
+ oh->yst_uid = obj->yst_uid;
+ oh->yst_gid = obj->yst_gid;
+ oh->yst_atime = obj->yst_atime;
+ oh->yst_mtime = obj->yst_mtime;
+ oh->yst_ctime = obj->yst_ctime;
+ oh->yst_rdev = obj->yst_rdev;
+#endif
+
+}
+
+void yaffs_attribs_init(struct yaffs_obj *obj, u32 gid, u32 uid, u32 rdev)
+{
+
+#ifdef CONFIG_YAFFS_WINCE
+ yfsd_win_file_time_now(obj->win_atime);
+ obj->win_ctime[0] = obj->win_mtime[0] = obj->win_atime[0];
+ obj->win_ctime[1] = obj->win_mtime[1] = obj->win_atime[1];
+
+#else
+ yaffs_load_current_time(obj, 1, 1);
+ obj->yst_rdev = rdev;
+ obj->yst_uid = uid;
+ obj->yst_gid = gid;
+#endif
+}
+
+void yaffs_load_current_time(struct yaffs_obj *obj, int do_a, int do_c)
+{
+#ifdef CONFIG_YAFFS_WINCE
+ yfsd_win_file_time_now(the_obj->win_atime);
+ the_obj->win_ctime[0] = the_obj->win_mtime[0] =
+ the_obj->win_atime[0];
+ the_obj->win_ctime[1] = the_obj->win_mtime[1] =
+ the_obj->win_atime[1];
+
+#else
+
+ obj->yst_mtime = Y_CURRENT_TIME;
+ if (do_a)
+ obj->yst_atime = obj->yst_atime;
+ if (do_c)
+ obj->yst_ctime = obj->yst_atime;
+#endif
+}
+
+loff_t yaffs_get_file_size(struct yaffs_obj *obj)
+{
+ YCHAR *alias = NULL;
+ obj = yaffs_get_equivalent_obj(obj);
+
+ switch (obj->variant_type) {
+ case YAFFS_OBJECT_TYPE_FILE:
+ return obj->variant.file_variant.file_size;
+ case YAFFS_OBJECT_TYPE_SYMLINK:
+ alias = obj->variant.symlink_variant.alias;
+ if (!alias)
+ return 0;
+ return yaffs_strnlen(alias, YAFFS_MAX_ALIAS_LENGTH);
+ default:
+ return 0;
+ }
+}
+
+int yaffs_set_attribs(struct yaffs_obj *obj, struct iattr *attr)
+{
+ unsigned int valid = attr->ia_valid;
+
+ if (valid & ATTR_MODE)
+ obj->yst_mode = attr->ia_mode;
+ if (valid & ATTR_UID)
+ obj->yst_uid = attr->ia_uid;
+ if (valid & ATTR_GID)
+ obj->yst_gid = attr->ia_gid;
+
+ if (valid & ATTR_ATIME)
+ obj->yst_atime = Y_TIME_CONVERT(attr->ia_atime);
+ if (valid & ATTR_CTIME)
+ obj->yst_ctime = Y_TIME_CONVERT(attr->ia_ctime);
+ if (valid & ATTR_MTIME)
+ obj->yst_mtime = Y_TIME_CONVERT(attr->ia_mtime);
+
+ if (valid & ATTR_SIZE)
+ yaffs_resize_file(obj, attr->ia_size);
+
+ yaffs_update_oh(obj, NULL, 1, 0, 0, NULL);
+
+ return YAFFS_OK;
+
+}
+
+int yaffs_get_attribs(struct yaffs_obj *obj, struct iattr *attr)
+{
+ unsigned int valid = 0;
+
+ attr->ia_mode = obj->yst_mode;
+ valid |= ATTR_MODE;
+ attr->ia_uid = obj->yst_uid;
+ valid |= ATTR_UID;
+ attr->ia_gid = obj->yst_gid;
+ valid |= ATTR_GID;
+
+ Y_TIME_CONVERT(attr->ia_atime) = obj->yst_atime;
+ valid |= ATTR_ATIME;
+ Y_TIME_CONVERT(attr->ia_ctime) = obj->yst_ctime;
+ valid |= ATTR_CTIME;
+ Y_TIME_CONVERT(attr->ia_mtime) = obj->yst_mtime;
+ valid |= ATTR_MTIME;
+
+ attr->ia_size = yaffs_get_file_size(obj);
+ valid |= ATTR_SIZE;
+
+ attr->ia_valid = valid;
+
+ return YAFFS_OK;
+}
diff --git a/roms/u-boot/fs/yaffs2/yaffs_attribs.h b/roms/u-boot/fs/yaffs2/yaffs_attribs.h
new file mode 100644
index 000000000..5b21b085b
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_attribs.h
@@ -0,0 +1,28 @@
+/*
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ */
+
+#ifndef __YAFFS_ATTRIBS_H__
+#define __YAFFS_ATTRIBS_H__
+
+#include "yaffs_guts.h"
+
+void yaffs_load_attribs(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh);
+void yaffs_load_attribs_oh(struct yaffs_obj_hdr *oh, struct yaffs_obj *obj);
+void yaffs_attribs_init(struct yaffs_obj *obj, u32 gid, u32 uid, u32 rdev);
+void yaffs_load_current_time(struct yaffs_obj *obj, int do_a, int do_c);
+int yaffs_set_attribs(struct yaffs_obj *obj, struct iattr *attr);
+int yaffs_get_attribs(struct yaffs_obj *obj, struct iattr *attr);
+
+#endif
diff --git a/roms/u-boot/fs/yaffs2/yaffs_bitmap.c b/roms/u-boot/fs/yaffs2/yaffs_bitmap.c
new file mode 100644
index 000000000..4440e930d
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_bitmap.c
@@ -0,0 +1,97 @@
+/*
+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "yaffs_bitmap.h"
+#include "yaffs_trace.h"
+/*
+ * Chunk bitmap manipulations
+ */
+
+static inline u8 *yaffs_block_bits(struct yaffs_dev *dev, int blk)
+{
+ if (blk < dev->internal_start_block || blk > dev->internal_end_block) {
+ yaffs_trace(YAFFS_TRACE_ERROR,
+ "BlockBits block %d is not valid",
+ blk);
+ BUG();
+ }
+ return dev->chunk_bits +
+ (dev->chunk_bit_stride * (blk - dev->internal_start_block));
+}
+
+void yaffs_verify_chunk_bit_id(struct yaffs_dev *dev, int blk, int chunk)
+{
+ if (blk < dev->internal_start_block || blk > dev->internal_end_block ||
+ chunk < 0 || chunk >= dev->param.chunks_per_block) {
+ yaffs_trace(YAFFS_TRACE_ERROR,
+ "Chunk Id (%d:%d) invalid",
+ blk, chunk);
+ BUG();
+ }
+}
+
+void yaffs_clear_chunk_bits(struct yaffs_dev *dev, int blk)
+{
+ u8 *blk_bits = yaffs_block_bits(dev, blk);
+
+ memset(blk_bits, 0, dev->chunk_bit_stride);
+}
+
+void yaffs_clear_chunk_bit(struct yaffs_dev *dev, int blk, int chunk)
+{
+ u8 *blk_bits = yaffs_block_bits(dev, blk);
+
+ yaffs_verify_chunk_bit_id(dev, blk, chunk);
+ blk_bits[chunk / 8] &= ~(1 << (chunk & 7));
+}
+
+void yaffs_set_chunk_bit(struct yaffs_dev *dev, int blk, int chunk)
+{
+ u8 *blk_bits = yaffs_block_bits(dev, blk);
+
+ yaffs_verify_chunk_bit_id(dev, blk, chunk);
+ blk_bits[chunk / 8] |= (1 << (chunk & 7));
+}
+
+int yaffs_check_chunk_bit(struct yaffs_dev *dev, int blk, int chunk)
+{
+ u8 *blk_bits = yaffs_block_bits(dev, blk);
+
+ yaffs_verify_chunk_bit_id(dev, blk, chunk);
+ return (blk_bits[chunk / 8] & (1 << (chunk & 7))) ? 1 : 0;
+}
+
+int yaffs_still_some_chunks(struct yaffs_dev *dev, int blk)
+{
+ u8 *blk_bits = yaffs_block_bits(dev, blk);
+ int i;
+
+ for (i = 0; i < dev->chunk_bit_stride; i++) {
+ if (*blk_bits)
+ return 1;
+ blk_bits++;
+ }
+ return 0;
+}
+
+int yaffs_count_chunk_bits(struct yaffs_dev *dev, int blk)
+{
+ u8 *blk_bits = yaffs_block_bits(dev, blk);
+ int i;
+ int n = 0;
+
+ for (i = 0; i < dev->chunk_bit_stride; i++, blk_bits++)
+ n += hweight8(*blk_bits);
+
+ return n;
+}
diff --git a/roms/u-boot/fs/yaffs2/yaffs_bitmap.h b/roms/u-boot/fs/yaffs2/yaffs_bitmap.h
new file mode 100644
index 000000000..e26b37d89
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_bitmap.h
@@ -0,0 +1,33 @@
+/*
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ */
+
+/*
+ * Chunk bitmap manipulations
+ */
+
+#ifndef __YAFFS_BITMAP_H__
+#define __YAFFS_BITMAP_H__
+
+#include "yaffs_guts.h"
+
+void yaffs_verify_chunk_bit_id(struct yaffs_dev *dev, int blk, int chunk);
+void yaffs_clear_chunk_bits(struct yaffs_dev *dev, int blk);
+void yaffs_clear_chunk_bit(struct yaffs_dev *dev, int blk, int chunk);
+void yaffs_set_chunk_bit(struct yaffs_dev *dev, int blk, int chunk);
+int yaffs_check_chunk_bit(struct yaffs_dev *dev, int blk, int chunk);
+int yaffs_still_some_chunks(struct yaffs_dev *dev, int blk);
+int yaffs_count_chunk_bits(struct yaffs_dev *dev, int blk);
+
+#endif
diff --git a/roms/u-boot/fs/yaffs2/yaffs_checkptrw.c b/roms/u-boot/fs/yaffs2/yaffs_checkptrw.c
new file mode 100644
index 000000000..628f02bb4
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_checkptrw.c
@@ -0,0 +1,409 @@
+/*
+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "yaffs_checkptrw.h"
+#include "yaffs_getblockinfo.h"
+#include <dm/devres.h>
+
+static int yaffs2_checkpt_space_ok(struct yaffs_dev *dev)
+{
+ int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks;
+
+ yaffs_trace(YAFFS_TRACE_CHECKPOINT,
+ "checkpt blocks_avail = %d", blocks_avail);
+
+ return (blocks_avail <= 0) ? 0 : 1;
+}
+
+static int yaffs_checkpt_erase(struct yaffs_dev *dev)
+{
+ int i;
+
+ if (!dev->param.erase_fn)
+ return 0;
+ yaffs_trace(YAFFS_TRACE_CHECKPOINT,
+ "checking blocks %d to %d",
+ dev->internal_start_block, dev->internal_end_block);
+
+ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) {
+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, i);
+ if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) {
+ yaffs_trace(YAFFS_TRACE_CHECKPOINT,
+ "erasing checkpt block %d", i);
+
+ dev->n_erasures++;
+
+ if (dev->param.
+ erase_fn(dev,
+ i - dev->block_offset /* realign */)) {
+ bi->block_state = YAFFS_BLOCK_STATE_EMPTY;
+ dev->n_erased_blocks++;
+ dev->n_free_chunks +=
+ dev->param.chunks_per_block;
+ } else {
+ dev->param.bad_block_fn(dev, i);
+ bi->block_state = YAFFS_BLOCK_STATE_DEAD;
+ }
+ }
+ }
+
+ dev->blocks_in_checkpt = 0;
+
+ return 1;
+}
+
+static void yaffs2_checkpt_find_erased_block(struct yaffs_dev *dev)
+{
+ int i;
+ int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks;
+
+ yaffs_trace(YAFFS_TRACE_CHECKPOINT,
+ "allocating checkpt block: erased %d reserved %d avail %d next %d ",
+ dev->n_erased_blocks, dev->param.n_reserved_blocks,
+ blocks_avail, dev->checkpt_next_block);
+
+ if (dev->checkpt_next_block >= 0 &&
+ dev->checkpt_next_block <= dev->internal_end_block &&
+ blocks_avail > 0) {
+
+ for (i = dev->checkpt_next_block; i <= dev->internal_end_block;
+ i++) {
+ struct yaffs_block_info *bi =
+ yaffs_get_block_info(dev, i);
+ if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) {
+ dev->checkpt_next_block = i + 1;
+ dev->checkpt_cur_block = i;
+ yaffs_trace(YAFFS_TRACE_CHECKPOINT,
+ "allocating checkpt block %d", i);
+ return;
+ }
+ }
+ }
+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, "out of checkpt blocks");
+
+ dev->checkpt_next_block = -1;
+ dev->checkpt_cur_block = -1;
+}
+
+static void yaffs2_checkpt_find_block(struct yaffs_dev *dev)
+{
+ int i;
+ struct yaffs_ext_tags tags;
+
+ yaffs_trace(YAFFS_TRACE_CHECKPOINT,
+ "find next checkpt block: start: blocks %d next %d",
+ dev->blocks_in_checkpt, dev->checkpt_next_block);
+
+ if (dev->blocks_in_checkpt < dev->checkpt_max_blocks)
+ for (i = dev->checkpt_next_block; i <= dev->internal_end_block;
+ i++) {
+ int chunk = i * dev->param.chunks_per_block;
+ int realigned_chunk = chunk - dev->chunk_offset;
+
+ dev->param.read_chunk_tags_fn(dev, realigned_chunk,
+ NULL, &tags);
+ yaffs_trace(YAFFS_TRACE_CHECKPOINT,
+ "find next checkpt block: search: block %d oid %d seq %d eccr %d",
+ i, tags.obj_id, tags.seq_number,
+ tags.ecc_result);
+
+ if (tags.seq_number == YAFFS_SEQUENCE_CHECKPOINT_DATA) {
+ /* Right kind of block */
+ dev->checkpt_next_block = tags.obj_id;
+ dev->checkpt_cur_block = i;
+ dev->checkpt_block_list[dev->
+ blocks_in_checkpt] = i;
+ dev->blocks_in_checkpt++;
+ yaffs_trace(YAFFS_TRACE_CHECKPOINT,
+ "found checkpt block %d", i);
+ return;
+ }
+ }
+
+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, "found no more checkpt blocks");
+
+ dev->checkpt_next_block = -1;
+ dev->checkpt_cur_block = -1;
+}
+
+int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing)
+{
+ int i;
+
+ dev->checkpt_open_write = writing;
+
+ /* Got the functions we need? */
+ if (!dev->param.write_chunk_tags_fn ||
+ !dev->param.read_chunk_tags_fn ||
+ !dev->param.erase_fn || !dev->param.bad_block_fn)
+ return 0;
+
+ if (writing && !yaffs2_checkpt_space_ok(dev))
+ return 0;
+
+ if (!dev->checkpt_buffer)
+ dev->checkpt_buffer =
+ kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS);
+ if (!dev->checkpt_buffer)
+ return 0;
+
+ dev->checkpt_page_seq = 0;
+ dev->checkpt_byte_count = 0;
+ dev->checkpt_sum = 0;
+ dev->checkpt_xor = 0;
+ dev->checkpt_cur_block = -1;
+ dev->checkpt_cur_chunk = -1;
+ dev->checkpt_next_block = dev->internal_start_block;
+
+ /* Erase all the blocks in the checkpoint area */
+ if (writing) {
+ memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk);
+ dev->checkpt_byte_offs = 0;
+ return yaffs_checkpt_erase(dev);
+ }
+
+ /* Set to a value that will kick off a read */
+ dev->checkpt_byte_offs = dev->data_bytes_per_chunk;
+ /* A checkpoint block list of 1 checkpoint block per 16 block is
+ * (hopefully) going to be way more than we need */
+ dev->blocks_in_checkpt = 0;
+ dev->checkpt_max_blocks =
+ (dev->internal_end_block - dev->internal_start_block) / 16 + 2;
+ dev->checkpt_block_list =
+ kmalloc(sizeof(int) * dev->checkpt_max_blocks, GFP_NOFS);
+
+ if (!dev->checkpt_block_list)
+ return 0;
+
+ for (i = 0; i < dev->checkpt_max_blocks; i++)
+ dev->checkpt_block_list[i] = -1;
+
+ return 1;
+}
+
+int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum)
+{
+ u32 composite_sum;
+
+ composite_sum = (dev->checkpt_sum << 8) | (dev->checkpt_xor & 0xff);
+ *sum = composite_sum;
+ return 1;
+}
+
+static int yaffs2_checkpt_flush_buffer(struct yaffs_dev *dev)
+{
+ int chunk;
+ int realigned_chunk;
+ struct yaffs_ext_tags tags;
+
+ if (dev->checkpt_cur_block < 0) {
+ yaffs2_checkpt_find_erased_block(dev);
+ dev->checkpt_cur_chunk = 0;
+ }
+
+ if (dev->checkpt_cur_block < 0)
+ return 0;
+
+ tags.is_deleted = 0;
+ tags.obj_id = dev->checkpt_next_block; /* Hint to next place to look */
+ tags.chunk_id = dev->checkpt_page_seq + 1;
+ tags.seq_number = YAFFS_SEQUENCE_CHECKPOINT_DATA;
+ tags.n_bytes = dev->data_bytes_per_chunk;
+ if (dev->checkpt_cur_chunk == 0) {
+ /* First chunk we write for the block? Set block state to
+ checkpoint */
+ struct yaffs_block_info *bi =
+ yaffs_get_block_info(dev, dev->checkpt_cur_block);
+ bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT;
+ dev->blocks_in_checkpt++;
+ }
+
+ chunk =
+ dev->checkpt_cur_block * dev->param.chunks_per_block +
+ dev->checkpt_cur_chunk;
+
+ yaffs_trace(YAFFS_TRACE_CHECKPOINT,
+ "checkpoint wite buffer nand %d(%d:%d) objid %d chId %d",
+ chunk, dev->checkpt_cur_block, dev->checkpt_cur_chunk,
+ tags.obj_id, tags.chunk_id);
+
+ realigned_chunk = chunk - dev->chunk_offset;
+
+ dev->n_page_writes++;
+
+ dev->param.write_chunk_tags_fn(dev, realigned_chunk,
+ dev->checkpt_buffer, &tags);
+ dev->checkpt_byte_offs = 0;
+ dev->checkpt_page_seq++;
+ dev->checkpt_cur_chunk++;
+ if (dev->checkpt_cur_chunk >= dev->param.chunks_per_block) {
+ dev->checkpt_cur_chunk = 0;
+ dev->checkpt_cur_block = -1;
+ }
+ memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk);
+
+ return 1;
+}
+
+int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes)
+{
+ int i = 0;
+ int ok = 1;
+ u8 *data_bytes = (u8 *) data;
+
+ if (!dev->checkpt_buffer)
+ return 0;
+
+ if (!dev->checkpt_open_write)
+ return -1;
+
+ while (i < n_bytes && ok) {
+ dev->checkpt_buffer[dev->checkpt_byte_offs] = *data_bytes;
+ dev->checkpt_sum += *data_bytes;
+ dev->checkpt_xor ^= *data_bytes;
+
+ dev->checkpt_byte_offs++;
+ i++;
+ data_bytes++;
+ dev->checkpt_byte_count++;
+
+ if (dev->checkpt_byte_offs < 0 ||
+ dev->checkpt_byte_offs >= dev->data_bytes_per_chunk)
+ ok = yaffs2_checkpt_flush_buffer(dev);
+ }
+
+ return i;
+}
+
+int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes)
+{
+ int i = 0;
+ int ok = 1;
+ struct yaffs_ext_tags tags;
+ int chunk;
+ int realigned_chunk;
+ u8 *data_bytes = (u8 *) data;
+
+ if (!dev->checkpt_buffer)
+ return 0;
+
+ if (dev->checkpt_open_write)
+ return -1;
+
+ while (i < n_bytes && ok) {
+
+ if (dev->checkpt_byte_offs < 0 ||
+ dev->checkpt_byte_offs >= dev->data_bytes_per_chunk) {
+
+ if (dev->checkpt_cur_block < 0) {
+ yaffs2_checkpt_find_block(dev);
+ dev->checkpt_cur_chunk = 0;
+ }
+
+ if (dev->checkpt_cur_block < 0) {
+ ok = 0;
+ break;
+ }
+
+ chunk = dev->checkpt_cur_block *
+ dev->param.chunks_per_block +
+ dev->checkpt_cur_chunk;
+
+ realigned_chunk = chunk - dev->chunk_offset;
+ dev->n_page_reads++;
+
+ /* read in the next chunk */
+ dev->param.read_chunk_tags_fn(dev,
+ realigned_chunk,
+ dev->checkpt_buffer,
+ &tags);
+
+ if (tags.chunk_id != (dev->checkpt_page_seq + 1) ||
+ tags.ecc_result > YAFFS_ECC_RESULT_FIXED ||
+ tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA) {
+ ok = 0;
+ break;
+ }
+
+ dev->checkpt_byte_offs = 0;
+ dev->checkpt_page_seq++;
+ dev->checkpt_cur_chunk++;
+
+ if (dev->checkpt_cur_chunk >=
+ dev->param.chunks_per_block)
+ dev->checkpt_cur_block = -1;
+ }
+
+ *data_bytes = dev->checkpt_buffer[dev->checkpt_byte_offs];
+ dev->checkpt_sum += *data_bytes;
+ dev->checkpt_xor ^= *data_bytes;
+ dev->checkpt_byte_offs++;
+ i++;
+ data_bytes++;
+ dev->checkpt_byte_count++;
+ }
+
+ return i;
+}
+
+int yaffs_checkpt_close(struct yaffs_dev *dev)
+{
+ int i;
+
+ if (dev->checkpt_open_write) {
+ if (dev->checkpt_byte_offs != 0)
+ yaffs2_checkpt_flush_buffer(dev);
+ } else if (dev->checkpt_block_list) {
+ for (i = 0;
+ i < dev->blocks_in_checkpt &&
+ dev->checkpt_block_list[i] >= 0; i++) {
+ int blk = dev->checkpt_block_list[i];
+ struct yaffs_block_info *bi = NULL;
+
+ if (dev->internal_start_block <= blk &&
+ blk <= dev->internal_end_block)
+ bi = yaffs_get_block_info(dev, blk);
+ if (bi && bi->block_state == YAFFS_BLOCK_STATE_EMPTY)
+ bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT;
+ }
+ kfree(dev->checkpt_block_list);
+ dev->checkpt_block_list = NULL;
+ }
+
+ dev->n_free_chunks -=
+ dev->blocks_in_checkpt * dev->param.chunks_per_block;
+ dev->n_erased_blocks -= dev->blocks_in_checkpt;
+
+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, "checkpoint byte count %d",
+ dev->checkpt_byte_count);
+
+ if (dev->checkpt_buffer) {
+ /* free the buffer */
+ kfree(dev->checkpt_buffer);
+ dev->checkpt_buffer = NULL;
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev)
+{
+ /* Erase the checkpoint data */
+
+ yaffs_trace(YAFFS_TRACE_CHECKPOINT,
+ "checkpoint invalidate of %d blocks",
+ dev->blocks_in_checkpt);
+
+ return yaffs_checkpt_erase(dev);
+}
diff --git a/roms/u-boot/fs/yaffs2/yaffs_checkptrw.h b/roms/u-boot/fs/yaffs2/yaffs_checkptrw.h
new file mode 100644
index 000000000..cdbaba715
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_checkptrw.h
@@ -0,0 +1,33 @@
+/*
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ */
+
+#ifndef __YAFFS_CHECKPTRW_H__
+#define __YAFFS_CHECKPTRW_H__
+
+#include "yaffs_guts.h"
+
+int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing);
+
+int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes);
+
+int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes);
+
+int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum);
+
+int yaffs_checkpt_close(struct yaffs_dev *dev);
+
+int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev);
+
+#endif
diff --git a/roms/u-boot/fs/yaffs2/yaffs_ecc.c b/roms/u-boot/fs/yaffs2/yaffs_ecc.c
new file mode 100644
index 000000000..9294107c1
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_ecc.c
@@ -0,0 +1,281 @@
+/*
+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/*
+ * This code implements the ECC algorithm used in SmartMedia.
+ *
+ * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes.
+ * The two unused bit are set to 1.
+ * The ECC can correct single bit errors in a 256-byte page of data. Thus, two
+ * such ECC blocks are used on a 512-byte NAND page.
+ *
+ */
+
+#include "yportenv.h"
+
+#include "yaffs_ecc.h"
+
+/* Table generated by gen-ecc.c
+ * Using a table means we do not have to calculate p1..p4 and p1'..p4'
+ * for each byte of data. These are instead provided in a table in bits7..2.
+ * Bit 0 of each entry indicates whether the entry has an odd or even parity,
+ * and therefore this bytes influence on the line parity.
+ */
+
+static const unsigned char column_parity_table[] = {
+ 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69,
+ 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00,
+ 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc,
+ 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95,
+ 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0,
+ 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99,
+ 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65,
+ 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c,
+ 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc,
+ 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5,
+ 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59,
+ 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30,
+ 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55,
+ 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c,
+ 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0,
+ 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9,
+ 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0,
+ 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9,
+ 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55,
+ 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c,
+ 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59,
+ 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30,
+ 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc,
+ 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5,
+ 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65,
+ 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c,
+ 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0,
+ 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99,
+ 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc,
+ 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95,
+ 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69,
+ 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00,
+};
+
+
+/* Calculate the ECC for a 256-byte block of data */
+void yaffs_ecc_calc(const unsigned char *data, unsigned char *ecc)
+{
+ unsigned int i;
+ unsigned char col_parity = 0;
+ unsigned char line_parity = 0;
+ unsigned char line_parity_prime = 0;
+ unsigned char t;
+ unsigned char b;
+
+ for (i = 0; i < 256; i++) {
+ b = column_parity_table[*data++];
+ col_parity ^= b;
+
+ if (b & 0x01) { /* odd number of bits in the byte */
+ line_parity ^= i;
+ line_parity_prime ^= ~i;
+ }
+ }
+
+ ecc[2] = (~col_parity) | 0x03;
+
+ t = 0;
+ if (line_parity & 0x80)
+ t |= 0x80;
+ if (line_parity_prime & 0x80)
+ t |= 0x40;
+ if (line_parity & 0x40)
+ t |= 0x20;
+ if (line_parity_prime & 0x40)
+ t |= 0x10;
+ if (line_parity & 0x20)
+ t |= 0x08;
+ if (line_parity_prime & 0x20)
+ t |= 0x04;
+ if (line_parity & 0x10)
+ t |= 0x02;
+ if (line_parity_prime & 0x10)
+ t |= 0x01;
+ ecc[1] = ~t;
+
+ t = 0;
+ if (line_parity & 0x08)
+ t |= 0x80;
+ if (line_parity_prime & 0x08)
+ t |= 0x40;
+ if (line_parity & 0x04)
+ t |= 0x20;
+ if (line_parity_prime & 0x04)
+ t |= 0x10;
+ if (line_parity & 0x02)
+ t |= 0x08;
+ if (line_parity_prime & 0x02)
+ t |= 0x04;
+ if (line_parity & 0x01)
+ t |= 0x02;
+ if (line_parity_prime & 0x01)
+ t |= 0x01;
+ ecc[0] = ~t;
+
+}
+
+/* Correct the ECC on a 256 byte block of data */
+
+int yaffs_ecc_correct(unsigned char *data, unsigned char *read_ecc,
+ const unsigned char *test_ecc)
+{
+ unsigned char d0, d1, d2; /* deltas */
+
+ d0 = read_ecc[0] ^ test_ecc[0];
+ d1 = read_ecc[1] ^ test_ecc[1];
+ d2 = read_ecc[2] ^ test_ecc[2];
+
+ if ((d0 | d1 | d2) == 0)
+ return 0; /* no error */
+
+ if (((d0 ^ (d0 >> 1)) & 0x55) == 0x55 &&
+ ((d1 ^ (d1 >> 1)) & 0x55) == 0x55 &&
+ ((d2 ^ (d2 >> 1)) & 0x54) == 0x54) {
+ /* Single bit (recoverable) error in data */
+
+ unsigned byte;
+ unsigned bit;
+
+ bit = byte = 0;
+
+ if (d1 & 0x80)
+ byte |= 0x80;
+ if (d1 & 0x20)
+ byte |= 0x40;
+ if (d1 & 0x08)
+ byte |= 0x20;
+ if (d1 & 0x02)
+ byte |= 0x10;
+ if (d0 & 0x80)
+ byte |= 0x08;
+ if (d0 & 0x20)
+ byte |= 0x04;
+ if (d0 & 0x08)
+ byte |= 0x02;
+ if (d0 & 0x02)
+ byte |= 0x01;
+
+ if (d2 & 0x80)
+ bit |= 0x04;
+ if (d2 & 0x20)
+ bit |= 0x02;
+ if (d2 & 0x08)
+ bit |= 0x01;
+
+ data[byte] ^= (1 << bit);
+
+ return 1; /* Corrected the error */
+ }
+
+ if ((hweight8(d0) + hweight8(d1) + hweight8(d2)) == 1) {
+ /* Reccoverable error in ecc */
+
+ read_ecc[0] = test_ecc[0];
+ read_ecc[1] = test_ecc[1];
+ read_ecc[2] = test_ecc[2];
+
+ return 1; /* Corrected the error */
+ }
+
+ /* Unrecoverable error */
+
+ return -1;
+
+}
+
+/*
+ * ECCxxxOther does ECC calcs on arbitrary n bytes of data
+ */
+void yaffs_ecc_calc_other(const unsigned char *data, unsigned n_bytes,
+ struct yaffs_ecc_other *ecc_other)
+{
+ unsigned int i;
+ unsigned char col_parity = 0;
+ unsigned line_parity = 0;
+ unsigned line_parity_prime = 0;
+ unsigned char b;
+
+ for (i = 0; i < n_bytes; i++) {
+ b = column_parity_table[*data++];
+ col_parity ^= b;
+
+ if (b & 0x01) {
+ /* odd number of bits in the byte */
+ line_parity ^= i;
+ line_parity_prime ^= ~i;
+ }
+
+ }
+
+ ecc_other->col_parity = (col_parity >> 2) & 0x3f;
+ ecc_other->line_parity = line_parity;
+ ecc_other->line_parity_prime = line_parity_prime;
+}
+
+int yaffs_ecc_correct_other(unsigned char *data, unsigned n_bytes,
+ struct yaffs_ecc_other *read_ecc,
+ const struct yaffs_ecc_other *test_ecc)
+{
+ unsigned char delta_col; /* column parity delta */
+ unsigned delta_line; /* line parity delta */
+ unsigned delta_line_prime; /* line parity delta */
+ unsigned bit;
+
+ delta_col = read_ecc->col_parity ^ test_ecc->col_parity;
+ delta_line = read_ecc->line_parity ^ test_ecc->line_parity;
+ delta_line_prime =
+ read_ecc->line_parity_prime ^ test_ecc->line_parity_prime;
+
+ if ((delta_col | delta_line | delta_line_prime) == 0)
+ return 0; /* no error */
+
+ if (delta_line == ~delta_line_prime &&
+ (((delta_col ^ (delta_col >> 1)) & 0x15) == 0x15)) {
+ /* Single bit (recoverable) error in data */
+
+ bit = 0;
+
+ if (delta_col & 0x20)
+ bit |= 0x04;
+ if (delta_col & 0x08)
+ bit |= 0x02;
+ if (delta_col & 0x02)
+ bit |= 0x01;
+
+ if (delta_line >= n_bytes)
+ return -1;
+
+ data[delta_line] ^= (1 << bit);
+
+ return 1; /* corrected */
+ }
+
+ if ((hweight32(delta_line) +
+ hweight32(delta_line_prime) +
+ hweight8(delta_col)) == 1) {
+ /* Reccoverable error in ecc */
+
+ *read_ecc = *test_ecc;
+ return 1; /* corrected */
+ }
+
+ /* Unrecoverable error */
+
+ return -1;
+}
diff --git a/roms/u-boot/fs/yaffs2/yaffs_ecc.h b/roms/u-boot/fs/yaffs2/yaffs_ecc.h
new file mode 100644
index 000000000..17d47bd80
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_ecc.h
@@ -0,0 +1,44 @@
+/*
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ */
+
+/*
+ * This code implements the ECC algorithm used in SmartMedia.
+ *
+ * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes.
+ * The two unused bit are set to 1.
+ * The ECC can correct single bit errors in a 256-byte page of data.
+ * Thus, two such ECC blocks are used on a 512-byte NAND page.
+ *
+ */
+
+#ifndef __YAFFS_ECC_H__
+#define __YAFFS_ECC_H__
+
+struct yaffs_ecc_other {
+ unsigned char col_parity;
+ unsigned line_parity;
+ unsigned line_parity_prime;
+};
+
+void yaffs_ecc_calc(const unsigned char *data, unsigned char *ecc);
+int yaffs_ecc_correct(unsigned char *data, unsigned char *read_ecc,
+ const unsigned char *test_ecc);
+
+void yaffs_ecc_calc_other(const unsigned char *data, unsigned n_bytes,
+ struct yaffs_ecc_other *ecc);
+int yaffs_ecc_correct_other(unsigned char *data, unsigned n_bytes,
+ struct yaffs_ecc_other *read_ecc,
+ const struct yaffs_ecc_other *test_ecc);
+#endif
diff --git a/roms/u-boot/fs/yaffs2/yaffs_error.c b/roms/u-boot/fs/yaffs2/yaffs_error.c
new file mode 100644
index 000000000..11b75f7a0
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_error.c
@@ -0,0 +1,58 @@
+/*
+ * YAFFS: Yet another FFS. A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Timothy Manning <timothy@yaffs.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "yaffsfs.h"
+
+struct error_entry {
+ int code;
+ const char *text;
+};
+
+static const struct error_entry error_list[] = {
+ { ENOMEM , "ENOMEM" },
+ { EBUSY , "EBUSY"},
+ { ENODEV , "ENODEV"},
+ { EINVAL , "EINVAL"},
+ { EBADF , "EBADF"},
+ { EACCES , "EACCES"},
+ { EXDEV , "EXDEV" },
+ { ENOENT , "ENOENT"},
+ { ENOSPC , "ENOSPC"},
+ { ERANGE , "ERANGE"},
+ { ENODATA, "ENODATA"},
+ { ENOTEMPTY, "ENOTEMPTY"},
+ { ENAMETOOLONG, "ENAMETOOLONG"},
+ { ENOMEM , "ENOMEM"},
+ { EEXIST , "EEXIST"},
+ { ENOTDIR , "ENOTDIR"},
+ { EISDIR , "EISDIR"},
+ { ENFILE, "ENFILE"},
+ { EROFS, "EROFS"},
+ { EFAULT, "EFAULT"},
+ { 0, NULL }
+};
+
+const char *yaffs_error_to_str(int err)
+{
+ const struct error_entry *e = error_list;
+
+ if (err < 0)
+ err = -err;
+
+ while (e->code && e->text) {
+ if (err == e->code)
+ return e->text;
+ e++;
+ }
+ return "Unknown error code";
+}
diff --git a/roms/u-boot/fs/yaffs2/yaffs_flashif.h b/roms/u-boot/fs/yaffs2/yaffs_flashif.h
new file mode 100644
index 000000000..e6e8979ee
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_flashif.h
@@ -0,0 +1,35 @@
+/*
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ */
+
+#ifndef __YAFFS_FLASH_H__
+#define __YAFFS_FLASH_H__
+
+
+#include "yaffs_guts.h"
+int yflash_EraseBlockInNAND(struct yaffs_dev *dev, int blockNumber);
+int yflash_WriteChunkToNAND(struct yaffs_dev *dev, int nand_chunk,
+ const u8 *data, const struct yaffs_spare *spare);
+int yflash_WriteChunkWithTagsToNAND(struct yaffs_dev *dev, int nand_chunk,
+ const u8 *data, const struct yaffs_ext_tags *tags);
+int yflash_ReadChunkFromNAND(struct yaffs_dev *dev, int nand_chunk,
+ u8 *data, struct yaffs_spare *spare);
+int yflash_ReadChunkWithTagsFromNAND(struct yaffs_dev *dev, int nand_chunk,
+ u8 *data, struct yaffs_ext_tags *tags);
+int yflash_InitialiseNAND(struct yaffs_dev *dev);
+int yflash_MarkNANDBlockBad(struct yaffs_dev *dev, int block_no);
+int yflash_QueryNANDBlock(struct yaffs_dev *dev, int block_no,
+ enum yaffs_block_state *state, u32 *seq_number);
+
+#endif
diff --git a/roms/u-boot/fs/yaffs2/yaffs_flashif2.h b/roms/u-boot/fs/yaffs2/yaffs_flashif2.h
new file mode 100644
index 000000000..cfdbde9dc
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_flashif2.h
@@ -0,0 +1,35 @@
+/*
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ */
+
+#ifndef __YAFFS_FLASH2_H__
+#define __YAFFS_FLASH2_H__
+
+
+#include "yaffs_guts.h"
+int yflash2_EraseBlockInNAND(struct yaffs_dev *dev, int blockNumber);
+int yflash2_WriteChunkToNAND(struct yaffs_dev *dev, int nand_chunk,
+ const u8 *data, const struct yaffs_spare *spare);
+int yflash2_WriteChunkWithTagsToNAND(struct yaffs_dev *dev, int nand_chunk,
+ const u8 *data, const struct yaffs_ext_tags *tags);
+int yflash2_ReadChunkFromNAND(struct yaffs_dev *dev, int nand_chunk,
+ u8 *data, struct yaffs_spare *spare);
+int yflash2_ReadChunkWithTagsFromNAND(struct yaffs_dev *dev, int nand_chunk,
+ u8 *data, struct yaffs_ext_tags *tags);
+int yflash2_InitialiseNAND(struct yaffs_dev *dev);
+int yflash2_MarkNANDBlockBad(struct yaffs_dev *dev, int block_no);
+int yflash2_QueryNANDBlock(struct yaffs_dev *dev, int block_no,
+ enum yaffs_block_state *state, u32 *seq_number);
+
+#endif
diff --git a/roms/u-boot/fs/yaffs2/yaffs_getblockinfo.h b/roms/u-boot/fs/yaffs2/yaffs_getblockinfo.h
new file mode 100644
index 000000000..8fd0802bd
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_getblockinfo.h
@@ -0,0 +1,35 @@
+/*
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ */
+
+#ifndef __YAFFS_GETBLOCKINFO_H__
+#define __YAFFS_GETBLOCKINFO_H__
+
+#include "yaffs_guts.h"
+#include "yaffs_trace.h"
+
+/* Function to manipulate block info */
+static inline struct yaffs_block_info *yaffs_get_block_info(struct yaffs_dev
+ *dev, int blk)
+{
+ if (blk < dev->internal_start_block || blk > dev->internal_end_block) {
+ yaffs_trace(YAFFS_TRACE_ERROR,
+ "**>> yaffs: get_block_info block %d is not valid",
+ blk);
+ BUG();
+ }
+ return &dev->block_info[blk - dev->internal_start_block];
+}
+
+#endif
diff --git a/roms/u-boot/fs/yaffs2/yaffs_guts.c b/roms/u-boot/fs/yaffs2/yaffs_guts.c
new file mode 100644
index 000000000..e13a73298
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_guts.c
@@ -0,0 +1,5009 @@
+/*
+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "yportenv.h"
+#include "yaffs_trace.h"
+#include <dm/devres.h>
+
+#include "yaffs_guts.h"
+#include "yaffs_getblockinfo.h"
+#include "yaffs_tagscompat.h"
+#include "yaffs_nand.h"
+#include "yaffs_yaffs1.h"
+#include "yaffs_yaffs2.h"
+#include "yaffs_bitmap.h"
+#include "yaffs_verify.h"
+#include "yaffs_nand.h"
+#include "yaffs_packedtags2.h"
+#include "yaffs_nameval.h"
+#include "yaffs_allocator.h"
+#include "yaffs_attribs.h"
+#include "yaffs_summary.h"
+
+/* Note YAFFS_GC_GOOD_ENOUGH must be <= YAFFS_GC_PASSIVE_THRESHOLD */
+#define YAFFS_GC_GOOD_ENOUGH 2
+#define YAFFS_GC_PASSIVE_THRESHOLD 4
+
+#include "yaffs_ecc.h"
+
+/* Forward declarations */
+
+static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk,
+ const u8 *buffer, int n_bytes, int use_reserve);
+
+
+
+/* Function to calculate chunk and offset */
+
+void yaffs_addr_to_chunk(struct yaffs_dev *dev, loff_t addr,
+ int *chunk_out, u32 *offset_out)
+{
+ int chunk;
+ u32 offset;
+
+ chunk = (u32) (addr >> dev->chunk_shift);
+
+ if (dev->chunk_div == 1) {
+ /* easy power of 2 case */
+ offset = (u32) (addr & dev->chunk_mask);
+ } else {
+ /* Non power-of-2 case */
+
+ loff_t chunk_base;
+
+ chunk /= dev->chunk_div;
+
+ chunk_base = ((loff_t) chunk) * dev->data_bytes_per_chunk;
+ offset = (u32) (addr - chunk_base);
+ }
+
+ *chunk_out = chunk;
+ *offset_out = offset;
+}
+
+/* Function to return the number of shifts for a power of 2 greater than or
+ * equal to the given number
+ * Note we don't try to cater for all possible numbers and this does not have to
+ * be hellishly efficient.
+ */
+
+static inline u32 calc_shifts_ceiling(u32 x)
+{
+ int extra_bits;
+ int shifts;
+
+ shifts = extra_bits = 0;
+
+ while (x > 1) {
+ if (x & 1)
+ extra_bits++;
+ x >>= 1;
+ shifts++;
+ }
+
+ if (extra_bits)
+ shifts++;
+
+ return shifts;
+}
+
+/* Function to return the number of shifts to get a 1 in bit 0
+ */
+
+static inline u32 calc_shifts(u32 x)
+{
+ u32 shifts;
+
+ shifts = 0;
+
+ if (!x)
+ return 0;
+
+ while (!(x & 1)) {
+ x >>= 1;
+ shifts++;
+ }
+
+ return shifts;
+}
+
+/*
+ * Temporary buffer manipulations.
+ */
+
+static int yaffs_init_tmp_buffers(struct yaffs_dev *dev)
+{
+ int i;
+ u8 *buf = (u8 *) 1;
+
+ memset(dev->temp_buffer, 0, sizeof(dev->temp_buffer));
+
+ for (i = 0; buf && i < YAFFS_N_TEMP_BUFFERS; i++) {
+ dev->temp_buffer[i].in_use = 0;
+ buf = kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS);
+ dev->temp_buffer[i].buffer = buf;
+ }
+
+ return buf ? YAFFS_OK : YAFFS_FAIL;
+}
+
+u8 *yaffs_get_temp_buffer(struct yaffs_dev * dev)
+{
+ int i;
+
+ dev->temp_in_use++;
+ if (dev->temp_in_use > dev->max_temp)
+ dev->max_temp = dev->temp_in_use;
+
+ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) {
+ if (dev->temp_buffer[i].in_use == 0) {
+ dev->temp_buffer[i].in_use = 1;
+ return dev->temp_buffer[i].buffer;
+ }
+ }
+
+ yaffs_trace(YAFFS_TRACE_BUFFERS, "Out of temp buffers");
+ /*
+ * If we got here then we have to allocate an unmanaged one
+ * This is not good.
+ */
+
+ dev->unmanaged_buffer_allocs++;
+ return kmalloc(dev->data_bytes_per_chunk, GFP_NOFS);
+
+}
+
+void yaffs_release_temp_buffer(struct yaffs_dev *dev, u8 *buffer)
+{
+ int i;
+
+ dev->temp_in_use--;
+
+ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) {
+ if (dev->temp_buffer[i].buffer == buffer) {
+ dev->temp_buffer[i].in_use = 0;
+ return;
+ }
+ }
+
+ if (buffer) {
+ /* assume it is an unmanaged one. */
+ yaffs_trace(YAFFS_TRACE_BUFFERS,
+ "Releasing unmanaged temp buffer");
+ kfree(buffer);
+ dev->unmanaged_buffer_deallocs++;
+ }
+
+}
+
+/*
+ * Determine if we have a managed buffer.
+ */
+int yaffs_is_managed_tmp_buffer(struct yaffs_dev *dev, const u8 *buffer)
+{
+ int i;
+
+ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) {
+ if (dev->temp_buffer[i].buffer == buffer)
+ return 1;
+ }
+
+ for (i = 0; i < dev->param.n_caches; i++) {
+ if (dev->cache[i].data == buffer)
+ return 1;
+ }
+
+ if (buffer == dev->checkpt_buffer)
+ return 1;
+
+ yaffs_trace(YAFFS_TRACE_ALWAYS,
+ "yaffs: unmaged buffer detected.");
+ return 0;
+}
+
+/*
+ * Functions for robustisizing TODO
+ *
+ */
+
+static void yaffs_handle_chunk_wr_ok(struct yaffs_dev *dev, int nand_chunk,
+ const u8 *data,
+ const struct yaffs_ext_tags *tags)
+{
+}
+
+static void yaffs_handle_chunk_update(struct yaffs_dev *dev, int nand_chunk,
+ const struct yaffs_ext_tags *tags)
+{
+}
+
+void yaffs_handle_chunk_error(struct yaffs_dev *dev,
+ struct yaffs_block_info *bi)
+{
+ if (!bi->gc_prioritise) {
+ bi->gc_prioritise = 1;
+ dev->has_pending_prioritised_gc = 1;
+ bi->chunk_error_strikes++;
+
+ if (bi->chunk_error_strikes > 3) {
+ bi->needs_retiring = 1; /* Too many stikes, so retire */
+ yaffs_trace(YAFFS_TRACE_ALWAYS,
+ "yaffs: Block struck out");
+
+ }
+ }
+}
+
+static void yaffs_handle_chunk_wr_error(struct yaffs_dev *dev, int nand_chunk,
+ int erased_ok)
+{
+ int flash_block = nand_chunk / dev->param.chunks_per_block;
+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, flash_block);
+
+ yaffs_handle_chunk_error(dev, bi);
+
+ if (erased_ok) {
+ /* Was an actual write failure,
+ * so mark the block for retirement.*/
+ bi->needs_retiring = 1;
+ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,
+ "**>> Block %d needs retiring", flash_block);
+ }
+
+ /* Delete the chunk */
+ yaffs_chunk_del(dev, nand_chunk, 1, __LINE__);
+ yaffs_skip_rest_of_block(dev);
+}
+
+/*
+ * Verification code
+ */
+
+/*
+ * Simple hash function. Needs to have a reasonable spread
+ */
+
+static inline int yaffs_hash_fn(int n)
+{
+ if (n < 0)
+ n = -n;
+ return n % YAFFS_NOBJECT_BUCKETS;
+}
+
+/*
+ * Access functions to useful fake objects.
+ * Note that root might have a presence in NAND if permissions are set.
+ */
+
+struct yaffs_obj *yaffs_root(struct yaffs_dev *dev)
+{
+ return dev->root_dir;
+}
+
+struct yaffs_obj *yaffs_lost_n_found(struct yaffs_dev *dev)
+{
+ return dev->lost_n_found;
+}
+
+/*
+ * Erased NAND checking functions
+ */
+
+int yaffs_check_ff(u8 *buffer, int n_bytes)
+{
+ /* Horrible, slow implementation */
+ while (n_bytes--) {
+ if (*buffer != 0xff)
+ return 0;
+ buffer++;
+ }
+ return 1;
+}
+
+static int yaffs_check_chunk_erased(struct yaffs_dev *dev, int nand_chunk)
+{
+ int retval = YAFFS_OK;
+ u8 *data = yaffs_get_temp_buffer(dev);
+ struct yaffs_ext_tags tags;
+
+ yaffs_rd_chunk_tags_nand(dev, nand_chunk, data, &tags);
+
+ if (tags.ecc_result > YAFFS_ECC_RESULT_NO_ERROR)
+ retval = YAFFS_FAIL;
+
+ if (!yaffs_check_ff(data, dev->data_bytes_per_chunk) ||
+ tags.chunk_used) {
+ yaffs_trace(YAFFS_TRACE_NANDACCESS,
+ "Chunk %d not erased", nand_chunk);
+ retval = YAFFS_FAIL;
+ }
+
+ yaffs_release_temp_buffer(dev, data);
+
+ return retval;
+
+}
+
+static int yaffs_verify_chunk_written(struct yaffs_dev *dev,
+ int nand_chunk,
+ const u8 *data,
+ struct yaffs_ext_tags *tags)
+{
+ int retval = YAFFS_OK;
+ struct yaffs_ext_tags temp_tags;
+ u8 *buffer = yaffs_get_temp_buffer(dev);
+
+ yaffs_rd_chunk_tags_nand(dev, nand_chunk, buffer, &temp_tags);
+ if (memcmp(buffer, data, dev->data_bytes_per_chunk) ||
+ temp_tags.obj_id != tags->obj_id ||
+ temp_tags.chunk_id != tags->chunk_id ||
+ temp_tags.n_bytes != tags->n_bytes)
+ retval = YAFFS_FAIL;
+
+ yaffs_release_temp_buffer(dev, buffer);
+
+ return retval;
+}
+
+
+int yaffs_check_alloc_available(struct yaffs_dev *dev, int n_chunks)
+{
+ int reserved_chunks;
+ int reserved_blocks = dev->param.n_reserved_blocks;
+ int checkpt_blocks;
+
+ checkpt_blocks = yaffs_calc_checkpt_blocks_required(dev);
+
+ reserved_chunks =
+ (reserved_blocks + checkpt_blocks) * dev->param.chunks_per_block;
+
+ return (dev->n_free_chunks > (reserved_chunks + n_chunks));
+}
+
+static int yaffs_find_alloc_block(struct yaffs_dev *dev)
+{
+ int i;
+ struct yaffs_block_info *bi;
+
+ if (dev->n_erased_blocks < 1) {
+ /* Hoosterman we've got a problem.
+ * Can't get space to gc
+ */
+ yaffs_trace(YAFFS_TRACE_ERROR,
+ "yaffs tragedy: no more erased blocks");
+
+ return -1;
+ }
+
+ /* Find an empty block. */
+
+ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) {
+ dev->alloc_block_finder++;
+ if (dev->alloc_block_finder < dev->internal_start_block
+ || dev->alloc_block_finder > dev->internal_end_block) {
+ dev->alloc_block_finder = dev->internal_start_block;
+ }
+
+ bi = yaffs_get_block_info(dev, dev->alloc_block_finder);
+
+ if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) {
+ bi->block_state = YAFFS_BLOCK_STATE_ALLOCATING;
+ dev->seq_number++;
+ bi->seq_number = dev->seq_number;
+ dev->n_erased_blocks--;
+ yaffs_trace(YAFFS_TRACE_ALLOCATE,
+ "Allocated block %d, seq %d, %d left" ,
+ dev->alloc_block_finder, dev->seq_number,
+ dev->n_erased_blocks);
+ return dev->alloc_block_finder;
+ }
+ }
+
+ yaffs_trace(YAFFS_TRACE_ALWAYS,
+ "yaffs tragedy: no more erased blocks, but there should have been %d",
+ dev->n_erased_blocks);
+
+ return -1;
+}
+
+static int yaffs_alloc_chunk(struct yaffs_dev *dev, int use_reserver,
+ struct yaffs_block_info **block_ptr)
+{
+ int ret_val;
+ struct yaffs_block_info *bi;
+
+ if (dev->alloc_block < 0) {
+ /* Get next block to allocate off */
+ dev->alloc_block = yaffs_find_alloc_block(dev);
+ dev->alloc_page = 0;
+ }
+
+ if (!use_reserver && !yaffs_check_alloc_available(dev, 1)) {
+ /* No space unless we're allowed to use the reserve. */
+ return -1;
+ }
+
+ if (dev->n_erased_blocks < dev->param.n_reserved_blocks
+ && dev->alloc_page == 0)
+ yaffs_trace(YAFFS_TRACE_ALLOCATE, "Allocating reserve");
+
+ /* Next page please.... */
+ if (dev->alloc_block >= 0) {
+ bi = yaffs_get_block_info(dev, dev->alloc_block);
+
+ ret_val = (dev->alloc_block * dev->param.chunks_per_block) +
+ dev->alloc_page;
+ bi->pages_in_use++;
+ yaffs_set_chunk_bit(dev, dev->alloc_block, dev->alloc_page);
+
+ dev->alloc_page++;
+
+ dev->n_free_chunks--;
+
+ /* If the block is full set the state to full */
+ if (dev->alloc_page >= dev->param.chunks_per_block) {
+ bi->block_state = YAFFS_BLOCK_STATE_FULL;
+ dev->alloc_block = -1;
+ }
+
+ if (block_ptr)
+ *block_ptr = bi;
+
+ return ret_val;
+ }
+
+ yaffs_trace(YAFFS_TRACE_ERROR,
+ "!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!");
+
+ return -1;
+}
+
+static int yaffs_get_erased_chunks(struct yaffs_dev *dev)
+{
+ int n;
+
+ n = dev->n_erased_blocks * dev->param.chunks_per_block;
+
+ if (dev->alloc_block > 0)
+ n += (dev->param.chunks_per_block - dev->alloc_page);
+
+ return n;
+
+}
+
+/*
+ * yaffs_skip_rest_of_block() skips over the rest of the allocation block
+ * if we don't want to write to it.
+ */
+void yaffs_skip_rest_of_block(struct yaffs_dev *dev)
+{
+ struct yaffs_block_info *bi;
+
+ if (dev->alloc_block > 0) {
+ bi = yaffs_get_block_info(dev, dev->alloc_block);
+ if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) {
+ bi->block_state = YAFFS_BLOCK_STATE_FULL;
+ dev->alloc_block = -1;
+ }
+ }
+}
+
+static int yaffs_write_new_chunk(struct yaffs_dev *dev,
+ const u8 *data,
+ struct yaffs_ext_tags *tags, int use_reserver)
+{
+ int attempts = 0;
+ int write_ok = 0;
+ int chunk;
+
+ yaffs2_checkpt_invalidate(dev);
+
+ do {
+ struct yaffs_block_info *bi = 0;
+ int erased_ok = 0;
+
+ chunk = yaffs_alloc_chunk(dev, use_reserver, &bi);
+ if (chunk < 0) {
+ /* no space */
+ break;
+ }
+
+ /* First check this chunk is erased, if it needs
+ * checking. The checking policy (unless forced
+ * always on) is as follows:
+ *
+ * Check the first page we try to write in a block.
+ * If the check passes then we don't need to check any
+ * more. If the check fails, we check again...
+ * If the block has been erased, we don't need to check.
+ *
+ * However, if the block has been prioritised for gc,
+ * then we think there might be something odd about
+ * this block and stop using it.
+ *
+ * Rationale: We should only ever see chunks that have
+ * not been erased if there was a partially written
+ * chunk due to power loss. This checking policy should
+ * catch that case with very few checks and thus save a
+ * lot of checks that are most likely not needed.
+ *
+ * Mods to the above
+ * If an erase check fails or the write fails we skip the
+ * rest of the block.
+ */
+
+ /* let's give it a try */
+ attempts++;
+
+ if (dev->param.always_check_erased)
+ bi->skip_erased_check = 0;
+
+ if (!bi->skip_erased_check) {
+ erased_ok = yaffs_check_chunk_erased(dev, chunk);
+ if (erased_ok != YAFFS_OK) {
+ yaffs_trace(YAFFS_TRACE_ERROR,
+ "**>> yaffs chunk %d was not erased",
+ chunk);
+
+ /* If not erased, delete this one,
+ * skip rest of block and
+ * try another chunk */
+ yaffs_chunk_del(dev, chunk, 1, __LINE__);
+ yaffs_skip_rest_of_block(dev);
+ continue;
+ }
+ }
+
+ write_ok = yaffs_wr_chunk_tags_nand(dev, chunk, data, tags);
+
+ if (!bi->skip_erased_check)
+ write_ok =
+ yaffs_verify_chunk_written(dev, chunk, data, tags);
+
+ if (write_ok != YAFFS_OK) {
+ /* Clean up aborted write, skip to next block and
+ * try another chunk */
+ yaffs_handle_chunk_wr_error(dev, chunk, erased_ok);
+ continue;
+ }
+
+ bi->skip_erased_check = 1;
+
+ /* Copy the data into the robustification buffer */
+ yaffs_handle_chunk_wr_ok(dev, chunk, data, tags);
+
+ } while (write_ok != YAFFS_OK &&
+ (yaffs_wr_attempts <= 0 || attempts <= yaffs_wr_attempts));
+
+ if (!write_ok)
+ chunk = -1;
+
+ if (attempts > 1) {
+ yaffs_trace(YAFFS_TRACE_ERROR,
+ "**>> yaffs write required %d attempts",
+ attempts);
+ dev->n_retried_writes += (attempts - 1);
+ }
+
+ return chunk;
+}
+
+/*
+ * Block retiring for handling a broken block.
+ */
+
+static void yaffs_retire_block(struct yaffs_dev *dev, int flash_block)
+{
+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, flash_block);
+
+ yaffs2_checkpt_invalidate(dev);
+
+ yaffs2_clear_oldest_dirty_seq(dev, bi);
+
+ if (yaffs_mark_bad(dev, flash_block) != YAFFS_OK) {
+ if (yaffs_erase_block(dev, flash_block) != YAFFS_OK) {
+ yaffs_trace(YAFFS_TRACE_ALWAYS,
+ "yaffs: Failed to mark bad and erase block %d",
+ flash_block);
+ } else {
+ struct yaffs_ext_tags tags;
+ int chunk_id =
+ flash_block * dev->param.chunks_per_block;
+
+ u8 *buffer = yaffs_get_temp_buffer(dev);
+
+ memset(buffer, 0xff, dev->data_bytes_per_chunk);
+ memset(&tags, 0, sizeof(tags));
+ tags.seq_number = YAFFS_SEQUENCE_BAD_BLOCK;
+ if (dev->param.write_chunk_tags_fn(dev, chunk_id -
+ dev->chunk_offset,
+ buffer,
+ &tags) != YAFFS_OK)
+ yaffs_trace(YAFFS_TRACE_ALWAYS,
+ "yaffs: Failed to write bad block marker to block %d",
+ flash_block);
+
+ yaffs_release_temp_buffer(dev, buffer);
+ }
+ }
+
+ bi->block_state = YAFFS_BLOCK_STATE_DEAD;
+ bi->gc_prioritise = 0;
+ bi->needs_retiring = 0;
+
+ dev->n_retired_blocks++;
+}
+
+/*---------------- Name handling functions ------------*/
+
+static u16 yaffs_calc_name_sum(const YCHAR *name)
+{
+ u16 sum = 0;
+ u16 i = 1;
+
+ if (!name)
+ return 0;
+
+ while ((*name) && i < (YAFFS_MAX_NAME_LENGTH / 2)) {
+
+ /* 0x1f mask is case insensitive */
+ sum += ((*name) & 0x1f) * i;
+ i++;
+ name++;
+ }
+ return sum;
+}
+
+void yaffs_set_obj_name(struct yaffs_obj *obj, const YCHAR * name)
+{
+ memset(obj->short_name, 0, sizeof(obj->short_name));
+ if (name &&
+ yaffs_strnlen(name, YAFFS_SHORT_NAME_LENGTH + 1) <=
+ YAFFS_SHORT_NAME_LENGTH)
+ yaffs_strcpy(obj->short_name, name);
+ else
+ obj->short_name[0] = _Y('\0');
+ obj->sum = yaffs_calc_name_sum(name);
+}
+
+void yaffs_set_obj_name_from_oh(struct yaffs_obj *obj,
+ const struct yaffs_obj_hdr *oh)
+{
+#ifdef CONFIG_YAFFS_AUTO_UNICODE
+ YCHAR tmp_name[YAFFS_MAX_NAME_LENGTH + 1];
+ memset(tmp_name, 0, sizeof(tmp_name));
+ yaffs_load_name_from_oh(obj->my_dev, tmp_name, oh->name,
+ YAFFS_MAX_NAME_LENGTH + 1);
+ yaffs_set_obj_name(obj, tmp_name);
+#else
+ yaffs_set_obj_name(obj, oh->name);
+#endif
+}
+
+loff_t yaffs_max_file_size(struct yaffs_dev *dev)
+{
+ return ((loff_t) YAFFS_MAX_CHUNK_ID) * dev->data_bytes_per_chunk;
+}
+
+/*-------------------- TNODES -------------------
+
+ * List of spare tnodes
+ * The list is hooked together using the first pointer
+ * in the tnode.
+ */
+
+struct yaffs_tnode *yaffs_get_tnode(struct yaffs_dev *dev)
+{
+ struct yaffs_tnode *tn = yaffs_alloc_raw_tnode(dev);
+
+ if (tn) {
+ memset(tn, 0, dev->tnode_size);
+ dev->n_tnodes++;
+ }
+
+ dev->checkpoint_blocks_required = 0; /* force recalculation */
+
+ return tn;
+}
+
+/* FreeTnode frees up a tnode and puts it back on the free list */
+static void yaffs_free_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn)
+{
+ yaffs_free_raw_tnode(dev, tn);
+ dev->n_tnodes--;
+ dev->checkpoint_blocks_required = 0; /* force recalculation */
+}
+
+static void yaffs_deinit_tnodes_and_objs(struct yaffs_dev *dev)
+{
+ yaffs_deinit_raw_tnodes_and_objs(dev);
+ dev->n_obj = 0;
+ dev->n_tnodes = 0;
+}
+
+void yaffs_load_tnode_0(struct yaffs_dev *dev, struct yaffs_tnode *tn,
+ unsigned pos, unsigned val)
+{
+ u32 *map = (u32 *) tn;
+ u32 bit_in_map;
+ u32 bit_in_word;
+ u32 word_in_map;
+ u32 mask;
+
+ pos &= YAFFS_TNODES_LEVEL0_MASK;
+ val >>= dev->chunk_grp_bits;
+
+ bit_in_map = pos * dev->tnode_width;
+ word_in_map = bit_in_map / 32;
+ bit_in_word = bit_in_map & (32 - 1);
+
+ mask = dev->tnode_mask << bit_in_word;
+
+ map[word_in_map] &= ~mask;
+ map[word_in_map] |= (mask & (val << bit_in_word));
+
+ if (dev->tnode_width > (32 - bit_in_word)) {
+ bit_in_word = (32 - bit_in_word);
+ word_in_map++;
+ mask =
+ dev->tnode_mask >> bit_in_word;
+ map[word_in_map] &= ~mask;
+ map[word_in_map] |= (mask & (val >> bit_in_word));
+ }
+}
+
+u32 yaffs_get_group_base(struct yaffs_dev *dev, struct yaffs_tnode *tn,
+ unsigned pos)
+{
+ u32 *map = (u32 *) tn;
+ u32 bit_in_map;
+ u32 bit_in_word;
+ u32 word_in_map;
+ u32 val;
+
+ pos &= YAFFS_TNODES_LEVEL0_MASK;
+
+ bit_in_map = pos * dev->tnode_width;
+ word_in_map = bit_in_map / 32;
+ bit_in_word = bit_in_map & (32 - 1);
+
+ val = map[word_in_map] >> bit_in_word;
+
+ if (dev->tnode_width > (32 - bit_in_word)) {
+ bit_in_word = (32 - bit_in_word);
+ word_in_map++;
+ val |= (map[word_in_map] << bit_in_word);
+ }
+
+ val &= dev->tnode_mask;
+ val <<= dev->chunk_grp_bits;
+
+ return val;
+}
+
+/* ------------------- End of individual tnode manipulation -----------------*/
+
+/* ---------Functions to manipulate the look-up tree (made up of tnodes) ------
+ * The look up tree is represented by the top tnode and the number of top_level
+ * in the tree. 0 means only the level 0 tnode is in the tree.
+ */
+
+/* FindLevel0Tnode finds the level 0 tnode, if one exists. */
+struct yaffs_tnode *yaffs_find_tnode_0(struct yaffs_dev *dev,
+ struct yaffs_file_var *file_struct,
+ u32 chunk_id)
+{
+ struct yaffs_tnode *tn = file_struct->top;
+ u32 i;
+ int required_depth;
+ int level = file_struct->top_level;
+
+ /* Check sane level and chunk Id */
+ if (level < 0 || level > YAFFS_TNODES_MAX_LEVEL)
+ return NULL;
+
+ if (chunk_id > YAFFS_MAX_CHUNK_ID)
+ return NULL;
+
+ /* First check we're tall enough (ie enough top_level) */
+
+ i = chunk_id >> YAFFS_TNODES_LEVEL0_BITS;
+ required_depth = 0;
+ while (i) {
+ i >>= YAFFS_TNODES_INTERNAL_BITS;
+ required_depth++;
+ }
+
+ if (required_depth > file_struct->top_level)
+ return NULL; /* Not tall enough, so we can't find it */
+
+ /* Traverse down to level 0 */
+ while (level > 0 && tn) {
+ tn = tn->internal[(chunk_id >>
+ (YAFFS_TNODES_LEVEL0_BITS +
+ (level - 1) *
+ YAFFS_TNODES_INTERNAL_BITS)) &
+ YAFFS_TNODES_INTERNAL_MASK];
+ level--;
+ }
+
+ return tn;
+}
+
+/* add_find_tnode_0 finds the level 0 tnode if it exists,
+ * otherwise first expands the tree.
+ * This happens in two steps:
+ * 1. If the tree isn't tall enough, then make it taller.
+ * 2. Scan down the tree towards the level 0 tnode adding tnodes if required.
+ *
+ * Used when modifying the tree.
+ *
+ * If the tn argument is NULL, then a fresh tnode will be added otherwise the
+ * specified tn will be plugged into the ttree.
+ */
+
+struct yaffs_tnode *yaffs_add_find_tnode_0(struct yaffs_dev *dev,
+ struct yaffs_file_var *file_struct,
+ u32 chunk_id,
+ struct yaffs_tnode *passed_tn)
+{
+ int required_depth;
+ int i;
+ int l;
+ struct yaffs_tnode *tn;
+ u32 x;
+
+ /* Check sane level and page Id */
+ if (file_struct->top_level < 0 ||
+ file_struct->top_level > YAFFS_TNODES_MAX_LEVEL)
+ return NULL;
+
+ if (chunk_id > YAFFS_MAX_CHUNK_ID)
+ return NULL;
+
+ /* First check we're tall enough (ie enough top_level) */
+
+ x = chunk_id >> YAFFS_TNODES_LEVEL0_BITS;
+ required_depth = 0;
+ while (x) {
+ x >>= YAFFS_TNODES_INTERNAL_BITS;
+ required_depth++;
+ }
+
+ if (required_depth > file_struct->top_level) {
+ /* Not tall enough, gotta make the tree taller */
+ for (i = file_struct->top_level; i < required_depth; i++) {
+
+ tn = yaffs_get_tnode(dev);
+
+ if (tn) {
+ tn->internal[0] = file_struct->top;
+ file_struct->top = tn;
+ file_struct->top_level++;
+ } else {
+ yaffs_trace(YAFFS_TRACE_ERROR,
+ "yaffs: no more tnodes");
+ return NULL;
+ }
+ }
+ }
+
+ /* Traverse down to level 0, adding anything we need */
+
+ l = file_struct->top_level;
+ tn = file_struct->top;
+
+ if (l > 0) {
+ while (l > 0 && tn) {
+ x = (chunk_id >>
+ (YAFFS_TNODES_LEVEL0_BITS +
+ (l - 1) * YAFFS_TNODES_INTERNAL_BITS)) &
+ YAFFS_TNODES_INTERNAL_MASK;
+
+ if ((l > 1) && !tn->internal[x]) {
+ /* Add missing non-level-zero tnode */
+ tn->internal[x] = yaffs_get_tnode(dev);
+ if (!tn->internal[x])
+ return NULL;
+ } else if (l == 1) {
+ /* Looking from level 1 at level 0 */
+ if (passed_tn) {
+ /* If we already have one, release it */
+ if (tn->internal[x])
+ yaffs_free_tnode(dev,
+ tn->internal[x]);
+ tn->internal[x] = passed_tn;
+
+ } else if (!tn->internal[x]) {
+ /* Don't have one, none passed in */
+ tn->internal[x] = yaffs_get_tnode(dev);
+ if (!tn->internal[x])
+ return NULL;
+ }
+ }
+
+ tn = tn->internal[x];
+ l--;
+ }
+ } else {
+ /* top is level 0 */
+ if (passed_tn) {
+ memcpy(tn, passed_tn,
+ (dev->tnode_width * YAFFS_NTNODES_LEVEL0) / 8);
+ yaffs_free_tnode(dev, passed_tn);
+ }
+ }
+
+ return tn;
+}
+
+static int yaffs_tags_match(const struct yaffs_ext_tags *tags, int obj_id,
+ int chunk_obj)
+{
+ return (tags->chunk_id == chunk_obj &&
+ tags->obj_id == obj_id &&
+ !tags->is_deleted) ? 1 : 0;
+
+}
+
+static int yaffs_find_chunk_in_group(struct yaffs_dev *dev, int the_chunk,
+ struct yaffs_ext_tags *tags, int obj_id,
+ int inode_chunk)
+{
+ int j;
+
+ for (j = 0; the_chunk && j < dev->chunk_grp_size; j++) {
+ if (yaffs_check_chunk_bit
+ (dev, the_chunk / dev->param.chunks_per_block,
+ the_chunk % dev->param.chunks_per_block)) {
+
+ if (dev->chunk_grp_size == 1)
+ return the_chunk;
+ else {
+ yaffs_rd_chunk_tags_nand(dev, the_chunk, NULL,
+ tags);
+ if (yaffs_tags_match(tags,
+ obj_id, inode_chunk)) {
+ /* found it; */
+ return the_chunk;
+ }
+ }
+ }
+ the_chunk++;
+ }
+ return -1;
+}
+
+static int yaffs_find_chunk_in_file(struct yaffs_obj *in, int inode_chunk,
+ struct yaffs_ext_tags *tags)
+{
+ /*Get the Tnode, then get the level 0 offset chunk offset */
+ struct yaffs_tnode *tn;
+ int the_chunk = -1;
+ struct yaffs_ext_tags local_tags;
+ int ret_val = -1;
+ struct yaffs_dev *dev = in->my_dev;
+
+ if (!tags) {
+ /* Passed a NULL, so use our own tags space */
+ tags = &local_tags;
+ }
+
+ tn = yaffs_find_tnode_0(dev, &in->variant.file_variant, inode_chunk);
+
+ if (!tn)
+ return ret_val;
+
+ the_chunk = yaffs_get_group_base(dev, tn, inode_chunk);
+
+ ret_val = yaffs_find_chunk_in_group(dev, the_chunk, tags, in->obj_id,
+ inode_chunk);
+ return ret_val;
+}
+
+static int yaffs_find_del_file_chunk(struct yaffs_obj *in, int inode_chunk,
+ struct yaffs_ext_tags *tags)
+{
+ /* Get the Tnode, then get the level 0 offset chunk offset */
+ struct yaffs_tnode *tn;
+ int the_chunk = -1;
+ struct yaffs_ext_tags local_tags;
+ struct yaffs_dev *dev = in->my_dev;
+ int ret_val = -1;
+
+ if (!tags) {
+ /* Passed a NULL, so use our own tags space */
+ tags = &local_tags;
+ }
+
+ tn = yaffs_find_tnode_0(dev, &in->variant.file_variant, inode_chunk);
+
+ if (!tn)
+ return ret_val;
+
+ the_chunk = yaffs_get_group_base(dev, tn, inode_chunk);
+
+ ret_val = yaffs_find_chunk_in_group(dev, the_chunk, tags, in->obj_id,
+ inode_chunk);
+
+ /* Delete the entry in the filestructure (if found) */
+ if (ret_val != -1)
+ yaffs_load_tnode_0(dev, tn, inode_chunk, 0);
+
+ return ret_val;
+}
+
+int yaffs_put_chunk_in_file(struct yaffs_obj *in, int inode_chunk,
+ int nand_chunk, int in_scan)
+{
+ /* NB in_scan is zero unless scanning.
+ * For forward scanning, in_scan is > 0;
+ * for backward scanning in_scan is < 0
+ *
+ * nand_chunk = 0 is a dummy insert to make sure the tnodes are there.
+ */
+
+ struct yaffs_tnode *tn;
+ struct yaffs_dev *dev = in->my_dev;
+ int existing_cunk;
+ struct yaffs_ext_tags existing_tags;
+ struct yaffs_ext_tags new_tags;
+ unsigned existing_serial, new_serial;
+
+ if (in->variant_type != YAFFS_OBJECT_TYPE_FILE) {
+ /* Just ignore an attempt at putting a chunk into a non-file
+ * during scanning.
+ * If it is not during Scanning then something went wrong!
+ */
+ if (!in_scan) {
+ yaffs_trace(YAFFS_TRACE_ERROR,
+ "yaffs tragedy:attempt to put data chunk into a non-file"
+ );
+ BUG();
+ }
+
+ yaffs_chunk_del(dev, nand_chunk, 1, __LINE__);
+ return YAFFS_OK;
+ }
+
+ tn = yaffs_add_find_tnode_0(dev,
+ &in->variant.file_variant,
+ inode_chunk, NULL);
+ if (!tn)
+ return YAFFS_FAIL;
+
+ if (!nand_chunk)
+ /* Dummy insert, bail now */
+ return YAFFS_OK;
+
+ existing_cunk = yaffs_get_group_base(dev, tn, inode_chunk);
+
+ if (in_scan != 0) {
+ /* If we're scanning then we need to test for duplicates
+ * NB This does not need to be efficient since it should only
+ * happen when the power fails during a write, then only one
+ * chunk should ever be affected.
+ *
+ * Correction for YAFFS2: This could happen quite a lot and we
+ * need to think about efficiency! TODO
+ * Update: For backward scanning we don't need to re-read tags
+ * so this is quite cheap.
+ */
+
+ if (existing_cunk > 0) {
+ /* NB Right now existing chunk will not be real
+ * chunk_id if the chunk group size > 1
+ * thus we have to do a FindChunkInFile to get the
+ * real chunk id.
+ *
+ * We have a duplicate now we need to decide which
+ * one to use:
+ *
+ * Backwards scanning YAFFS2: The old one is what
+ * we use, dump the new one.
+ * YAFFS1: Get both sets of tags and compare serial
+ * numbers.
+ */
+
+ if (in_scan > 0) {
+ /* Only do this for forward scanning */
+ yaffs_rd_chunk_tags_nand(dev,
+ nand_chunk,
+ NULL, &new_tags);
+
+ /* Do a proper find */
+ existing_cunk =
+ yaffs_find_chunk_in_file(in, inode_chunk,
+ &existing_tags);
+ }
+
+ if (existing_cunk <= 0) {
+ /*Hoosterman - how did this happen? */
+
+ yaffs_trace(YAFFS_TRACE_ERROR,
+ "yaffs tragedy: existing chunk < 0 in scan"
+ );
+
+ }
+
+ /* NB The deleted flags should be false, otherwise
+ * the chunks will not be loaded during a scan
+ */
+
+ if (in_scan > 0) {
+ new_serial = new_tags.serial_number;
+ existing_serial = existing_tags.serial_number;
+ }
+
+ if ((in_scan > 0) &&
+ (existing_cunk <= 0 ||
+ ((existing_serial + 1) & 3) == new_serial)) {
+ /* Forward scanning.
+ * Use new
+ * Delete the old one and drop through to
+ * update the tnode
+ */
+ yaffs_chunk_del(dev, existing_cunk, 1,
+ __LINE__);
+ } else {
+ /* Backward scanning or we want to use the
+ * existing one
+ * Delete the new one and return early so that
+ * the tnode isn't changed
+ */
+ yaffs_chunk_del(dev, nand_chunk, 1, __LINE__);
+ return YAFFS_OK;
+ }
+ }
+
+ }
+
+ if (existing_cunk == 0)
+ in->n_data_chunks++;
+
+ yaffs_load_tnode_0(dev, tn, inode_chunk, nand_chunk);
+
+ return YAFFS_OK;
+}
+
+static void yaffs_soft_del_chunk(struct yaffs_dev *dev, int chunk)
+{
+ struct yaffs_block_info *the_block;
+ unsigned block_no;
+
+ yaffs_trace(YAFFS_TRACE_DELETION, "soft delete chunk %d", chunk);
+
+ block_no = chunk / dev->param.chunks_per_block;
+ the_block = yaffs_get_block_info(dev, block_no);
+ if (the_block) {
+ the_block->soft_del_pages++;
+ dev->n_free_chunks++;
+ yaffs2_update_oldest_dirty_seq(dev, block_no, the_block);
+ }
+}
+
+/* SoftDeleteWorker scans backwards through the tnode tree and soft deletes all
+ * the chunks in the file.
+ * All soft deleting does is increment the block's softdelete count and pulls
+ * the chunk out of the tnode.
+ * Thus, essentially this is the same as DeleteWorker except that the chunks
+ * are soft deleted.
+ */
+
+static int yaffs_soft_del_worker(struct yaffs_obj *in, struct yaffs_tnode *tn,
+ u32 level, int chunk_offset)
+{
+ int i;
+ int the_chunk;
+ int all_done = 1;
+ struct yaffs_dev *dev = in->my_dev;
+
+ if (!tn)
+ return 1;
+
+ if (level > 0) {
+ for (i = YAFFS_NTNODES_INTERNAL - 1;
+ all_done && i >= 0;
+ i--) {
+ if (tn->internal[i]) {
+ all_done =
+ yaffs_soft_del_worker(in,
+ tn->internal[i],
+ level - 1,
+ (chunk_offset <<
+ YAFFS_TNODES_INTERNAL_BITS)
+ + i);
+ if (all_done) {
+ yaffs_free_tnode(dev,
+ tn->internal[i]);
+ tn->internal[i] = NULL;
+ } else {
+ /* Can this happen? */
+ }
+ }
+ }
+ return (all_done) ? 1 : 0;
+ }
+
+ /* level 0 */
+ for (i = YAFFS_NTNODES_LEVEL0 - 1; i >= 0; i--) {
+ the_chunk = yaffs_get_group_base(dev, tn, i);
+ if (the_chunk) {
+ yaffs_soft_del_chunk(dev, the_chunk);
+ yaffs_load_tnode_0(dev, tn, i, 0);
+ }
+ }
+ return 1;
+}
+
+static void yaffs_remove_obj_from_dir(struct yaffs_obj *obj)
+{
+ struct yaffs_dev *dev = obj->my_dev;
+ struct yaffs_obj *parent;
+
+ yaffs_verify_obj_in_dir(obj);
+ parent = obj->parent;
+
+ yaffs_verify_dir(parent);
+
+ if (dev && dev->param.remove_obj_fn)
+ dev->param.remove_obj_fn(obj);
+
+ list_del_init(&obj->siblings);
+ obj->parent = NULL;
+
+ yaffs_verify_dir(parent);
+}
+
+void yaffs_add_obj_to_dir(struct yaffs_obj *directory, struct yaffs_obj *obj)
+{
+ if (!directory) {
+ yaffs_trace(YAFFS_TRACE_ALWAYS,
+ "tragedy: Trying to add an object to a null pointer directory"
+ );
+ BUG();
+ return;
+ }
+ if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {
+ yaffs_trace(YAFFS_TRACE_ALWAYS,
+ "tragedy: Trying to add an object to a non-directory"
+ );
+ BUG();
+ }
+
+ if (obj->siblings.prev == NULL) {
+ /* Not initialised */
+ BUG();
+ }
+
+ yaffs_verify_dir(directory);
+
+ yaffs_remove_obj_from_dir(obj);
+
+ /* Now add it */
+ list_add(&obj->siblings, &directory->variant.dir_variant.children);
+ obj->parent = directory;
+
+ if (directory == obj->my_dev->unlinked_dir
+ || directory == obj->my_dev->del_dir) {
+ obj->unlinked = 1;
+ obj->my_dev->n_unlinked_files++;
+ obj->rename_allowed = 0;
+ }
+
+ yaffs_verify_dir(directory);
+ yaffs_verify_obj_in_dir(obj);
+}
+
+static int yaffs_change_obj_name(struct yaffs_obj *obj,
+ struct yaffs_obj *new_dir,
+ const YCHAR *new_name, int force, int shadows)
+{
+ int unlink_op;
+ int del_op;
+ struct yaffs_obj *existing_target;
+
+ if (new_dir == NULL)
+ new_dir = obj->parent; /* use the old directory */
+
+ if (new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {
+ yaffs_trace(YAFFS_TRACE_ALWAYS,
+ "tragedy: yaffs_change_obj_name: new_dir is not a directory"
+ );
+ BUG();
+ }
+
+ unlink_op = (new_dir == obj->my_dev->unlinked_dir);
+ del_op = (new_dir == obj->my_dev->del_dir);
+
+ existing_target = yaffs_find_by_name(new_dir, new_name);
+
+ /* If the object is a file going into the unlinked directory,
+ * then it is OK to just stuff it in since duplicate names are OK.
+ * else only proceed if the new name does not exist and we're putting
+ * it into a directory.
+ */
+ if (!(unlink_op || del_op || force ||
+ shadows > 0 || !existing_target) ||
+ new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY)
+ return YAFFS_FAIL;
+
+ yaffs_set_obj_name(obj, new_name);
+ obj->dirty = 1;
+ yaffs_add_obj_to_dir(new_dir, obj);
+
+ if (unlink_op)
+ obj->unlinked = 1;
+
+ /* If it is a deletion then we mark it as a shrink for gc */
+ if (yaffs_update_oh(obj, new_name, 0, del_op, shadows, NULL) >= 0)
+ return YAFFS_OK;
+
+ return YAFFS_FAIL;
+}
+
+/*------------------------ Short Operations Cache ------------------------------
+ * In many situations where there is no high level buffering a lot of
+ * reads might be short sequential reads, and a lot of writes may be short
+ * sequential writes. eg. scanning/writing a jpeg file.
+ * In these cases, a short read/write cache can provide a huge perfomance
+ * benefit with dumb-as-a-rock code.
+ * In Linux, the page cache provides read buffering and the short op cache
+ * provides write buffering.
+ *
+ * There are a small number (~10) of cache chunks per device so that we don't
+ * need a very intelligent search.
+ */
+
+static int yaffs_obj_cache_dirty(struct yaffs_obj *obj)
+{
+ struct yaffs_dev *dev = obj->my_dev;
+ int i;
+ struct yaffs_cache *cache;
+ int n_caches = obj->my_dev->param.n_caches;
+
+ for (i = 0; i < n_caches; i++) {
+ cache = &dev->cache[i];
+ if (cache->object == obj && cache->dirty)
+ return 1;
+ }
+
+ return 0;
+}
+
+static void yaffs_flush_file_cache(struct yaffs_obj *obj)
+{
+ struct yaffs_dev *dev = obj->my_dev;
+ int lowest = -99; /* Stop compiler whining. */
+ int i;
+ struct yaffs_cache *cache;
+ int chunk_written = 0;
+ int n_caches = obj->my_dev->param.n_caches;
+
+ if (n_caches < 1)
+ return;
+ do {
+ cache = NULL;
+
+ /* Find the lowest dirty chunk for this object */
+ for (i = 0; i < n_caches; i++) {
+ if (dev->cache[i].object == obj &&
+ dev->cache[i].dirty) {
+ if (!cache ||
+ dev->cache[i].chunk_id < lowest) {
+ cache = &dev->cache[i];
+ lowest = cache->chunk_id;
+ }
+ }
+ }
+
+ if (cache && !cache->locked) {
+ /* Write it out and free it up */
+ chunk_written =
+ yaffs_wr_data_obj(cache->object,
+ cache->chunk_id,
+ cache->data,
+ cache->n_bytes, 1);
+ cache->dirty = 0;
+ cache->object = NULL;
+ }
+ } while (cache && chunk_written > 0);
+
+ if (cache)
+ /* Hoosterman, disk full while writing cache out. */
+ yaffs_trace(YAFFS_TRACE_ERROR,
+ "yaffs tragedy: no space during cache write");
+}
+
+/*yaffs_flush_whole_cache(dev)
+ *
+ *
+ */
+
+void yaffs_flush_whole_cache(struct yaffs_dev *dev)
+{
+ struct yaffs_obj *obj;
+ int n_caches = dev->param.n_caches;
+ int i;
+
+ /* Find a dirty object in the cache and flush it...
+ * until there are no further dirty objects.
+ */
+ do {
+ obj = NULL;
+ for (i = 0; i < n_caches && !obj; i++) {
+ if (dev->cache[i].object && dev->cache[i].dirty)
+ obj = dev->cache[i].object;
+ }
+ if (obj)
+ yaffs_flush_file_cache(obj);
+ } while (obj);
+
+}
+
+/* Grab us a cache chunk for use.
+ * First look for an empty one.
+ * Then look for the least recently used non-dirty one.
+ * Then look for the least recently used dirty one...., flush and look again.
+ */
+static struct yaffs_cache *yaffs_grab_chunk_worker(struct yaffs_dev *dev)
+{
+ int i;
+
+ if (dev->param.n_caches > 0) {
+ for (i = 0; i < dev->param.n_caches; i++) {
+ if (!dev->cache[i].object)
+ return &dev->cache[i];
+ }
+ }
+ return NULL;
+}
+
+static struct yaffs_cache *yaffs_grab_chunk_cache(struct yaffs_dev *dev)
+{
+ struct yaffs_cache *cache;
+ struct yaffs_obj *the_obj;
+ int usage;
+ int i;
+
+ if (dev->param.n_caches < 1)
+ return NULL;
+
+ /* Try find a non-dirty one... */
+
+ cache = yaffs_grab_chunk_worker(dev);
+
+ if (!cache) {
+ /* They were all dirty, find the LRU object and flush
+ * its cache, then find again.
+ * NB what's here is not very accurate,
+ * we actually flush the object with the LRU chunk.
+ */
+
+ /* With locking we can't assume we can use entry zero,
+ * Set the_obj to a valid pointer for Coverity. */
+ the_obj = dev->cache[0].object;
+ usage = -1;
+ cache = NULL;
+
+ for (i = 0; i < dev->param.n_caches; i++) {
+ if (dev->cache[i].object &&
+ !dev->cache[i].locked &&
+ (dev->cache[i].last_use < usage ||
+ !cache)) {
+ usage = dev->cache[i].last_use;
+ the_obj = dev->cache[i].object;
+ cache = &dev->cache[i];
+ }
+ }
+
+ if (!cache || cache->dirty) {
+ /* Flush and try again */
+ yaffs_flush_file_cache(the_obj);
+ cache = yaffs_grab_chunk_worker(dev);
+ }
+ }
+ return cache;
+}
+
+/* Find a cached chunk */
+static struct yaffs_cache *yaffs_find_chunk_cache(const struct yaffs_obj *obj,
+ int chunk_id)
+{
+ struct yaffs_dev *dev = obj->my_dev;
+ int i;
+
+ if (dev->param.n_caches < 1)
+ return NULL;
+
+ for (i = 0; i < dev->param.n_caches; i++) {
+ if (dev->cache[i].object == obj &&
+ dev->cache[i].chunk_id == chunk_id) {
+ dev->cache_hits++;
+
+ return &dev->cache[i];
+ }
+ }
+ return NULL;
+}
+
+/* Mark the chunk for the least recently used algorithym */
+static void yaffs_use_cache(struct yaffs_dev *dev, struct yaffs_cache *cache,
+ int is_write)
+{
+ int i;
+
+ if (dev->param.n_caches < 1)
+ return;
+
+ if (dev->cache_last_use < 0 ||
+ dev->cache_last_use > 100000000) {
+ /* Reset the cache usages */
+ for (i = 1; i < dev->param.n_caches; i++)
+ dev->cache[i].last_use = 0;
+
+ dev->cache_last_use = 0;
+ }
+ dev->cache_last_use++;
+ cache->last_use = dev->cache_last_use;
+
+ if (is_write)
+ cache->dirty = 1;
+}
+
+/* Invalidate a single cache page.
+ * Do this when a whole page gets written,
+ * ie the short cache for this page is no longer valid.
+ */
+static void yaffs_invalidate_chunk_cache(struct yaffs_obj *object, int chunk_id)
+{
+ struct yaffs_cache *cache;
+
+ if (object->my_dev->param.n_caches > 0) {
+ cache = yaffs_find_chunk_cache(object, chunk_id);
+
+ if (cache)
+ cache->object = NULL;
+ }
+}
+
+/* Invalidate all the cache pages associated with this object
+ * Do this whenever ther file is deleted or resized.
+ */
+static void yaffs_invalidate_whole_cache(struct yaffs_obj *in)
+{
+ int i;
+ struct yaffs_dev *dev = in->my_dev;
+
+ if (dev->param.n_caches > 0) {
+ /* Invalidate it. */
+ for (i = 0; i < dev->param.n_caches; i++) {
+ if (dev->cache[i].object == in)
+ dev->cache[i].object = NULL;
+ }
+ }
+}
+
+static void yaffs_unhash_obj(struct yaffs_obj *obj)
+{
+ int bucket;
+ struct yaffs_dev *dev = obj->my_dev;
+
+ /* If it is still linked into the bucket list, free from the list */
+ if (!list_empty(&obj->hash_link)) {
+ list_del_init(&obj->hash_link);
+ bucket = yaffs_hash_fn(obj->obj_id);
+ dev->obj_bucket[bucket].count--;
+ }
+}
+
+/* FreeObject frees up a Object and puts it back on the free list */
+static void yaffs_free_obj(struct yaffs_obj *obj)
+{
+ struct yaffs_dev *dev;
+
+ if (!obj) {
+ BUG();
+ return;
+ }
+ dev = obj->my_dev;
+ yaffs_trace(YAFFS_TRACE_OS, "FreeObject %p inode %p",
+ obj, obj->my_inode);
+ if (obj->parent)
+ BUG();
+ if (!list_empty(&obj->siblings))
+ BUG();
+
+ if (obj->my_inode) {
+ /* We're still hooked up to a cached inode.
+ * Don't delete now, but mark for later deletion
+ */
+ obj->defered_free = 1;
+ return;
+ }
+
+ yaffs_unhash_obj(obj);
+
+ yaffs_free_raw_obj(dev, obj);
+ dev->n_obj--;
+ dev->checkpoint_blocks_required = 0; /* force recalculation */
+}
+
+void yaffs_handle_defered_free(struct yaffs_obj *obj)
+{
+ if (obj->defered_free)
+ yaffs_free_obj(obj);
+}
+
+static int yaffs_generic_obj_del(struct yaffs_obj *in)
+{
+ /* Iinvalidate the file's data in the cache, without flushing. */
+ yaffs_invalidate_whole_cache(in);
+
+ if (in->my_dev->param.is_yaffs2 && in->parent != in->my_dev->del_dir) {
+ /* Move to unlinked directory so we have a deletion record */
+ yaffs_change_obj_name(in, in->my_dev->del_dir, _Y("deleted"), 0,
+ 0);
+ }
+
+ yaffs_remove_obj_from_dir(in);
+ yaffs_chunk_del(in->my_dev, in->hdr_chunk, 1, __LINE__);
+ in->hdr_chunk = 0;
+
+ yaffs_free_obj(in);
+ return YAFFS_OK;
+
+}
+
+static void yaffs_soft_del_file(struct yaffs_obj *obj)
+{
+ if (!obj->deleted ||
+ obj->variant_type != YAFFS_OBJECT_TYPE_FILE ||
+ obj->soft_del)
+ return;
+
+ if (obj->n_data_chunks <= 0) {
+ /* Empty file with no duplicate object headers,
+ * just delete it immediately */
+ yaffs_free_tnode(obj->my_dev, obj->variant.file_variant.top);
+ obj->variant.file_variant.top = NULL;
+ yaffs_trace(YAFFS_TRACE_TRACING,
+ "yaffs: Deleting empty file %d",
+ obj->obj_id);
+ yaffs_generic_obj_del(obj);
+ } else {
+ yaffs_soft_del_worker(obj,
+ obj->variant.file_variant.top,
+ obj->variant.
+ file_variant.top_level, 0);
+ obj->soft_del = 1;
+ }
+}
+
+/* Pruning removes any part of the file structure tree that is beyond the
+ * bounds of the file (ie that does not point to chunks).
+ *
+ * A file should only get pruned when its size is reduced.
+ *
+ * Before pruning, the chunks must be pulled from the tree and the
+ * level 0 tnode entries must be zeroed out.
+ * Could also use this for file deletion, but that's probably better handled
+ * by a special case.
+ *
+ * This function is recursive. For levels > 0 the function is called again on
+ * any sub-tree. For level == 0 we just check if the sub-tree has data.
+ * If there is no data in a subtree then it is pruned.
+ */
+
+static struct yaffs_tnode *yaffs_prune_worker(struct yaffs_dev *dev,
+ struct yaffs_tnode *tn, u32 level,
+ int del0)
+{
+ int i;
+ int has_data;
+
+ if (!tn)
+ return tn;
+
+ has_data = 0;
+
+ if (level > 0) {
+ for (i = 0; i < YAFFS_NTNODES_INTERNAL; i++) {
+ if (tn->internal[i]) {
+ tn->internal[i] =
+ yaffs_prune_worker(dev,
+ tn->internal[i],
+ level - 1,
+ (i == 0) ? del0 : 1);
+ }
+
+ if (tn->internal[i])
+ has_data++;
+ }
+ } else {
+ int tnode_size_u32 = dev->tnode_size / sizeof(u32);
+ u32 *map = (u32 *) tn;
+
+ for (i = 0; !has_data && i < tnode_size_u32; i++) {
+ if (map[i])
+ has_data++;
+ }
+ }
+
+ if (has_data == 0 && del0) {
+ /* Free and return NULL */
+ yaffs_free_tnode(dev, tn);
+ tn = NULL;
+ }
+ return tn;
+}
+
+static int yaffs_prune_tree(struct yaffs_dev *dev,
+ struct yaffs_file_var *file_struct)
+{
+ int i;
+ int has_data;
+ int done = 0;
+ struct yaffs_tnode *tn;
+
+ if (file_struct->top_level < 1)
+ return YAFFS_OK;
+
+ file_struct->top =
+ yaffs_prune_worker(dev, file_struct->top, file_struct->top_level, 0);
+
+ /* Now we have a tree with all the non-zero branches NULL but
+ * the height is the same as it was.
+ * Let's see if we can trim internal tnodes to shorten the tree.
+ * We can do this if only the 0th element in the tnode is in use
+ * (ie all the non-zero are NULL)
+ */
+
+ while (file_struct->top_level && !done) {
+ tn = file_struct->top;
+
+ has_data = 0;
+ for (i = 1; i < YAFFS_NTNODES_INTERNAL; i++) {
+ if (tn->internal[i])
+ has_data++;
+ }
+
+ if (!has_data) {
+ file_struct->top = tn->internal[0];
+ file_struct->top_level--;
+ yaffs_free_tnode(dev, tn);
+ } else {
+ done = 1;
+ }
+ }
+
+ return YAFFS_OK;
+}
+
+/*-------------------- End of File Structure functions.-------------------*/
+
+/* alloc_empty_obj gets us a clean Object.*/
+static struct yaffs_obj *yaffs_alloc_empty_obj(struct yaffs_dev *dev)
+{
+ struct yaffs_obj *obj = yaffs_alloc_raw_obj(dev);
+
+ if (!obj)
+ return obj;
+
+ dev->n_obj++;
+
+ /* Now sweeten it up... */
+
+ memset(obj, 0, sizeof(struct yaffs_obj));
+ obj->being_created = 1;
+
+ obj->my_dev = dev;
+ obj->hdr_chunk = 0;
+ obj->variant_type = YAFFS_OBJECT_TYPE_UNKNOWN;
+ INIT_LIST_HEAD(&(obj->hard_links));
+ INIT_LIST_HEAD(&(obj->hash_link));
+ INIT_LIST_HEAD(&obj->siblings);
+
+ /* Now make the directory sane */
+ if (dev->root_dir) {
+ obj->parent = dev->root_dir;
+ list_add(&(obj->siblings),
+ &dev->root_dir->variant.dir_variant.children);
+ }
+
+ /* Add it to the lost and found directory.
+ * NB Can't put root or lost-n-found in lost-n-found so
+ * check if lost-n-found exists first
+ */
+ if (dev->lost_n_found)
+ yaffs_add_obj_to_dir(dev->lost_n_found, obj);
+
+ obj->being_created = 0;
+
+ dev->checkpoint_blocks_required = 0; /* force recalculation */
+
+ return obj;
+}
+
+static int yaffs_find_nice_bucket(struct yaffs_dev *dev)
+{
+ int i;
+ int l = 999;
+ int lowest = 999999;
+
+ /* Search for the shortest list or one that
+ * isn't too long.
+ */
+
+ for (i = 0; i < 10 && lowest > 4; i++) {
+ dev->bucket_finder++;
+ dev->bucket_finder %= YAFFS_NOBJECT_BUCKETS;
+ if (dev->obj_bucket[dev->bucket_finder].count < lowest) {
+ lowest = dev->obj_bucket[dev->bucket_finder].count;
+ l = dev->bucket_finder;
+ }
+ }
+
+ return l;
+}
+
+static int yaffs_new_obj_id(struct yaffs_dev *dev)
+{
+ int bucket = yaffs_find_nice_bucket(dev);
+ int found = 0;
+ struct list_head *i;
+ u32 n = (u32) bucket;
+
+ /* Now find an object value that has not already been taken
+ * by scanning the list.
+ */
+
+ while (!found) {
+ found = 1;
+ n += YAFFS_NOBJECT_BUCKETS;
+ list_for_each(i, &dev->obj_bucket[bucket].list) {
+ /* If there is already one in the list */
+ if (list_entry(i, struct yaffs_obj,
+ hash_link)->obj_id == n) {
+ found = 0;
+ break;
+ }
+ }
+ }
+ return n;
+}
+
+static void yaffs_hash_obj(struct yaffs_obj *in)
+{
+ int bucket = yaffs_hash_fn(in->obj_id);
+ struct yaffs_dev *dev = in->my_dev;
+
+ list_add(&in->hash_link, &dev->obj_bucket[bucket].list);
+ dev->obj_bucket[bucket].count++;
+}
+
+struct yaffs_obj *yaffs_find_by_number(struct yaffs_dev *dev, u32 number)
+{
+ int bucket = yaffs_hash_fn(number);
+ struct list_head *i;
+ struct yaffs_obj *in;
+
+ list_for_each(i, &dev->obj_bucket[bucket].list) {
+ /* Look if it is in the list */
+ in = list_entry(i, struct yaffs_obj, hash_link);
+ if (in->obj_id == number) {
+ /* Don't show if it is defered free */
+ if (in->defered_free)
+ return NULL;
+ return in;
+ }
+ }
+
+ return NULL;
+}
+
+struct yaffs_obj *yaffs_new_obj(struct yaffs_dev *dev, int number,
+ enum yaffs_obj_type type)
+{
+ struct yaffs_obj *the_obj = NULL;
+ struct yaffs_tnode *tn = NULL;
+
+ if (number < 0)
+ number = yaffs_new_obj_id(dev);
+
+ if (type == YAFFS_OBJECT_TYPE_FILE) {
+ tn = yaffs_get_tnode(dev);
+ if (!tn)
+ return NULL;
+ }
+
+ the_obj = yaffs_alloc_empty_obj(dev);
+ if (!the_obj) {
+ if (tn)
+ yaffs_free_tnode(dev, tn);
+ return NULL;
+ }
+
+ the_obj->fake = 0;
+ the_obj->rename_allowed = 1;
+ the_obj->unlink_allowed = 1;
+ the_obj->obj_id = number;
+ yaffs_hash_obj(the_obj);
+ the_obj->variant_type = type;
+ yaffs_load_current_time(the_obj, 1, 1);
+
+ switch (type) {
+ case YAFFS_OBJECT_TYPE_FILE:
+ the_obj->variant.file_variant.file_size = 0;
+ the_obj->variant.file_variant.scanned_size = 0;
+ the_obj->variant.file_variant.shrink_size =
+ yaffs_max_file_size(dev);
+ the_obj->variant.file_variant.top_level = 0;
+ the_obj->variant.file_variant.top = tn;
+ break;
+ case YAFFS_OBJECT_TYPE_DIRECTORY:
+ INIT_LIST_HEAD(&the_obj->variant.dir_variant.children);
+ INIT_LIST_HEAD(&the_obj->variant.dir_variant.dirty);
+ break;
+ case YAFFS_OBJECT_TYPE_SYMLINK:
+ case YAFFS_OBJECT_TYPE_HARDLINK:
+ case YAFFS_OBJECT_TYPE_SPECIAL:
+ /* No action required */
+ break;
+ case YAFFS_OBJECT_TYPE_UNKNOWN:
+ /* todo this should not happen */
+ break;
+ }
+ return the_obj;
+}
+
+static struct yaffs_obj *yaffs_create_fake_dir(struct yaffs_dev *dev,
+ int number, u32 mode)
+{
+
+ struct yaffs_obj *obj =
+ yaffs_new_obj(dev, number, YAFFS_OBJECT_TYPE_DIRECTORY);
+
+ if (!obj)
+ return NULL;
+
+ obj->fake = 1; /* it is fake so it might not use NAND */
+ obj->rename_allowed = 0;
+ obj->unlink_allowed = 0;
+ obj->deleted = 0;
+ obj->unlinked = 0;
+ obj->yst_mode = mode;
+ obj->my_dev = dev;
+ obj->hdr_chunk = 0; /* Not a valid chunk. */
+ return obj;
+
+}
+
+
+static void yaffs_init_tnodes_and_objs(struct yaffs_dev *dev)
+{
+ int i;
+
+ dev->n_obj = 0;
+ dev->n_tnodes = 0;
+ yaffs_init_raw_tnodes_and_objs(dev);
+
+ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) {
+ INIT_LIST_HEAD(&dev->obj_bucket[i].list);
+ dev->obj_bucket[i].count = 0;
+ }
+}
+
+struct yaffs_obj *yaffs_find_or_create_by_number(struct yaffs_dev *dev,
+ int number,
+ enum yaffs_obj_type type)
+{
+ struct yaffs_obj *the_obj = NULL;
+
+ if (number > 0)
+ the_obj = yaffs_find_by_number(dev, number);
+
+ if (!the_obj)
+ the_obj = yaffs_new_obj(dev, number, type);
+
+ return the_obj;
+
+}
+
+YCHAR *yaffs_clone_str(const YCHAR *str)
+{
+ YCHAR *new_str = NULL;
+ int len;
+
+ if (!str)
+ str = _Y("");
+
+ len = yaffs_strnlen(str, YAFFS_MAX_ALIAS_LENGTH);
+ new_str = kmalloc((len + 1) * sizeof(YCHAR), GFP_NOFS);
+ if (new_str) {
+ yaffs_strncpy(new_str, str, len);
+ new_str[len] = 0;
+ }
+ return new_str;
+
+}
+/*
+ *yaffs_update_parent() handles fixing a directories mtime and ctime when a new
+ * link (ie. name) is created or deleted in the directory.
+ *
+ * ie.
+ * create dir/a : update dir's mtime/ctime
+ * rm dir/a: update dir's mtime/ctime
+ * modify dir/a: don't update dir's mtimme/ctime
+ *
+ * This can be handled immediately or defered. Defering helps reduce the number
+ * of updates when many files in a directory are changed within a brief period.
+ *
+ * If the directory updating is defered then yaffs_update_dirty_dirs must be
+ * called periodically.
+ */
+
+static void yaffs_update_parent(struct yaffs_obj *obj)
+{
+ struct yaffs_dev *dev;
+
+ if (!obj)
+ return;
+ dev = obj->my_dev;
+ obj->dirty = 1;
+ yaffs_load_current_time(obj, 0, 1);
+ if (dev->param.defered_dir_update) {
+ struct list_head *link = &obj->variant.dir_variant.dirty;
+
+ if (list_empty(link)) {
+ list_add(link, &dev->dirty_dirs);
+ yaffs_trace(YAFFS_TRACE_BACKGROUND,
+ "Added object %d to dirty directories",
+ obj->obj_id);
+ }
+
+ } else {
+ yaffs_update_oh(obj, NULL, 0, 0, 0, NULL);
+ }
+}
+
+void yaffs_update_dirty_dirs(struct yaffs_dev *dev)
+{
+ struct list_head *link;
+ struct yaffs_obj *obj;
+ struct yaffs_dir_var *d_s;
+ union yaffs_obj_var *o_v;
+
+ yaffs_trace(YAFFS_TRACE_BACKGROUND, "Update dirty directories");
+
+ while (!list_empty(&dev->dirty_dirs)) {
+ link = dev->dirty_dirs.next;
+ list_del_init(link);
+
+ d_s = list_entry(link, struct yaffs_dir_var, dirty);
+ o_v = list_entry(d_s, union yaffs_obj_var, dir_variant);
+ obj = list_entry(o_v, struct yaffs_obj, variant);
+
+ yaffs_trace(YAFFS_TRACE_BACKGROUND, "Update directory %d",
+ obj->obj_id);
+
+ if (obj->dirty)
+ yaffs_update_oh(obj, NULL, 0, 0, 0, NULL);
+ }
+}
+
+/*
+ * Mknod (create) a new object.
+ * equiv_obj only has meaning for a hard link;
+ * alias_str only has meaning for a symlink.
+ * rdev only has meaning for devices (a subset of special objects)
+ */
+
+static struct yaffs_obj *yaffs_create_obj(enum yaffs_obj_type type,
+ struct yaffs_obj *parent,
+ const YCHAR *name,
+ u32 mode,
+ u32 uid,
+ u32 gid,
+ struct yaffs_obj *equiv_obj,
+ const YCHAR *alias_str, u32 rdev)
+{
+ struct yaffs_obj *in;
+ YCHAR *str = NULL;
+ struct yaffs_dev *dev = parent->my_dev;
+
+ /* Check if the entry exists.
+ * If it does then fail the call since we don't want a dup. */
+ if (yaffs_find_by_name(parent, name))
+ return NULL;
+
+ if (type == YAFFS_OBJECT_TYPE_SYMLINK) {
+ str = yaffs_clone_str(alias_str);
+ if (!str)
+ return NULL;
+ }
+
+ in = yaffs_new_obj(dev, -1, type);
+
+ if (!in) {
+ kfree(str);
+ return NULL;
+ }
+
+ in->hdr_chunk = 0;
+ in->valid = 1;
+ in->variant_type = type;
+
+ in->yst_mode = mode;
+
+ yaffs_attribs_init(in, gid, uid, rdev);
+
+ in->n_data_chunks = 0;
+
+ yaffs_set_obj_name(in, name);
+ in->dirty = 1;
+
+ yaffs_add_obj_to_dir(parent, in);
+
+ in->my_dev = parent->my_dev;
+
+ switch (type) {
+ case YAFFS_OBJECT_TYPE_SYMLINK:
+ in->variant.symlink_variant.alias = str;
+ break;
+ case YAFFS_OBJECT_TYPE_HARDLINK:
+ in->variant.hardlink_variant.equiv_obj = equiv_obj;
+ in->variant.hardlink_variant.equiv_id = equiv_obj->obj_id;
+ list_add(&in->hard_links, &equiv_obj->hard_links);
+ break;
+ case YAFFS_OBJECT_TYPE_FILE:
+ case YAFFS_OBJECT_TYPE_DIRECTORY:
+ case YAFFS_OBJECT_TYPE_SPECIAL:
+ case YAFFS_OBJECT_TYPE_UNKNOWN:
+ /* do nothing */
+ break;
+ }
+
+ if (yaffs_update_oh(in, name, 0, 0, 0, NULL) < 0) {
+ /* Could not create the object header, fail */
+ yaffs_del_obj(in);
+ in = NULL;
+ }
+
+ if (in)
+ yaffs_update_parent(parent);
+
+ return in;
+}
+
+struct yaffs_obj *yaffs_create_file(struct yaffs_obj *parent,
+ const YCHAR *name, u32 mode, u32 uid,
+ u32 gid)
+{
+ return yaffs_create_obj(YAFFS_OBJECT_TYPE_FILE, parent, name, mode,
+ uid, gid, NULL, NULL, 0);
+}
+
+struct yaffs_obj *yaffs_create_dir(struct yaffs_obj *parent, const YCHAR *name,
+ u32 mode, u32 uid, u32 gid)
+{
+ return yaffs_create_obj(YAFFS_OBJECT_TYPE_DIRECTORY, parent, name,
+ mode, uid, gid, NULL, NULL, 0);
+}
+
+struct yaffs_obj *yaffs_create_special(struct yaffs_obj *parent,
+ const YCHAR *name, u32 mode, u32 uid,
+ u32 gid, u32 rdev)
+{
+ return yaffs_create_obj(YAFFS_OBJECT_TYPE_SPECIAL, parent, name, mode,
+ uid, gid, NULL, NULL, rdev);
+}
+
+struct yaffs_obj *yaffs_create_symlink(struct yaffs_obj *parent,
+ const YCHAR *name, u32 mode, u32 uid,
+ u32 gid, const YCHAR *alias)
+{
+ return yaffs_create_obj(YAFFS_OBJECT_TYPE_SYMLINK, parent, name, mode,
+ uid, gid, NULL, alias, 0);
+}
+
+/* yaffs_link_obj returns the object id of the equivalent object.*/
+struct yaffs_obj *yaffs_link_obj(struct yaffs_obj *parent, const YCHAR * name,
+ struct yaffs_obj *equiv_obj)
+{
+ /* Get the real object in case we were fed a hard link obj */
+ equiv_obj = yaffs_get_equivalent_obj(equiv_obj);
+
+ if (yaffs_create_obj(YAFFS_OBJECT_TYPE_HARDLINK,
+ parent, name, 0, 0, 0,
+ equiv_obj, NULL, 0))
+ return equiv_obj;
+
+ return NULL;
+
+}
+
+
+
+/*---------------------- Block Management and Page Allocation -------------*/
+
+static void yaffs_deinit_blocks(struct yaffs_dev *dev)
+{
+ if (dev->block_info_alt && dev->block_info)
+ vfree(dev->block_info);
+ else
+ kfree(dev->block_info);
+
+ dev->block_info_alt = 0;
+
+ dev->block_info = NULL;
+
+ if (dev->chunk_bits_alt && dev->chunk_bits)
+ vfree(dev->chunk_bits);
+ else
+ kfree(dev->chunk_bits);
+ dev->chunk_bits_alt = 0;
+ dev->chunk_bits = NULL;
+}
+
+static int yaffs_init_blocks(struct yaffs_dev *dev)
+{
+ int n_blocks = dev->internal_end_block - dev->internal_start_block + 1;
+
+ dev->block_info = NULL;
+ dev->chunk_bits = NULL;
+ dev->alloc_block = -1; /* force it to get a new one */
+
+ /* If the first allocation strategy fails, thry the alternate one */
+ dev->block_info =
+ kmalloc(n_blocks * sizeof(struct yaffs_block_info), GFP_NOFS);
+ if (!dev->block_info) {
+ dev->block_info =
+ vmalloc(n_blocks * sizeof(struct yaffs_block_info));
+ dev->block_info_alt = 1;
+ } else {
+ dev->block_info_alt = 0;
+ }
+
+ if (!dev->block_info)
+ goto alloc_error;
+
+ /* Set up dynamic blockinfo stuff. Round up bytes. */
+ dev->chunk_bit_stride = (dev->param.chunks_per_block + 7) / 8;
+ dev->chunk_bits =
+ kmalloc(dev->chunk_bit_stride * n_blocks, GFP_NOFS);
+ if (!dev->chunk_bits) {
+ dev->chunk_bits =
+ vmalloc(dev->chunk_bit_stride * n_blocks);
+ dev->chunk_bits_alt = 1;
+ } else {
+ dev->chunk_bits_alt = 0;
+ }
+ if (!dev->chunk_bits)
+ goto alloc_error;
+
+
+ memset(dev->block_info, 0, n_blocks * sizeof(struct yaffs_block_info));
+ memset(dev->chunk_bits, 0, dev->chunk_bit_stride * n_blocks);
+ return YAFFS_OK;
+
+alloc_error:
+ yaffs_deinit_blocks(dev);
+ return YAFFS_FAIL;
+}
+
+
+void yaffs_block_became_dirty(struct yaffs_dev *dev, int block_no)
+{
+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, block_no);
+ int erased_ok = 0;
+ int i;
+
+ /* If the block is still healthy erase it and mark as clean.
+ * If the block has had a data failure, then retire it.
+ */
+
+ yaffs_trace(YAFFS_TRACE_GC | YAFFS_TRACE_ERASE,
+ "yaffs_block_became_dirty block %d state %d %s",
+ block_no, bi->block_state,
+ (bi->needs_retiring) ? "needs retiring" : "");
+
+ yaffs2_clear_oldest_dirty_seq(dev, bi);
+
+ bi->block_state = YAFFS_BLOCK_STATE_DIRTY;
+
+ /* If this is the block being garbage collected then stop gc'ing */
+ if (block_no == dev->gc_block)
+ dev->gc_block = 0;
+
+ /* If this block is currently the best candidate for gc
+ * then drop as a candidate */
+ if (block_no == dev->gc_dirtiest) {
+ dev->gc_dirtiest = 0;
+ dev->gc_pages_in_use = 0;
+ }
+
+ if (!bi->needs_retiring) {
+ yaffs2_checkpt_invalidate(dev);
+ erased_ok = yaffs_erase_block(dev, block_no);
+ if (!erased_ok) {
+ dev->n_erase_failures++;
+ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,
+ "**>> Erasure failed %d", block_no);
+ }
+ }
+
+ /* Verify erasure if needed */
+ if (erased_ok &&
+ ((yaffs_trace_mask & YAFFS_TRACE_ERASE) ||
+ !yaffs_skip_verification(dev))) {
+ for (i = 0; i < dev->param.chunks_per_block; i++) {
+ if (!yaffs_check_chunk_erased(dev,
+ block_no * dev->param.chunks_per_block + i)) {
+ yaffs_trace(YAFFS_TRACE_ERROR,
+ ">>Block %d erasure supposedly OK, but chunk %d not erased",
+ block_no, i);
+ }
+ }
+ }
+
+ if (!erased_ok) {
+ /* We lost a block of free space */
+ dev->n_free_chunks -= dev->param.chunks_per_block;
+ yaffs_retire_block(dev, block_no);
+ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,
+ "**>> Block %d retired", block_no);
+ return;
+ }
+
+ /* Clean it up... */
+ bi->block_state = YAFFS_BLOCK_STATE_EMPTY;
+ bi->seq_number = 0;
+ dev->n_erased_blocks++;
+ bi->pages_in_use = 0;
+ bi->soft_del_pages = 0;
+ bi->has_shrink_hdr = 0;
+ bi->skip_erased_check = 1; /* Clean, so no need to check */
+ bi->gc_prioritise = 0;
+ bi->has_summary = 0;
+
+ yaffs_clear_chunk_bits(dev, block_no);
+
+ yaffs_trace(YAFFS_TRACE_ERASE, "Erased block %d", block_no);
+}
+
+static inline int yaffs_gc_process_chunk(struct yaffs_dev *dev,
+ struct yaffs_block_info *bi,
+ int old_chunk, u8 *buffer)
+{
+ int new_chunk;
+ int mark_flash = 1;
+ struct yaffs_ext_tags tags;
+ struct yaffs_obj *object;
+ int matching_chunk;
+ int ret_val = YAFFS_OK;
+
+ memset(&tags, 0, sizeof(tags));
+ yaffs_rd_chunk_tags_nand(dev, old_chunk,
+ buffer, &tags);
+ object = yaffs_find_by_number(dev, tags.obj_id);
+
+ yaffs_trace(YAFFS_TRACE_GC_DETAIL,
+ "Collecting chunk in block %d, %d %d %d ",
+ dev->gc_chunk, tags.obj_id,
+ tags.chunk_id, tags.n_bytes);
+
+ if (object && !yaffs_skip_verification(dev)) {
+ if (tags.chunk_id == 0)
+ matching_chunk =
+ object->hdr_chunk;
+ else if (object->soft_del)
+ /* Defeat the test */
+ matching_chunk = old_chunk;
+ else
+ matching_chunk =
+ yaffs_find_chunk_in_file
+ (object, tags.chunk_id,
+ NULL);
+
+ if (old_chunk != matching_chunk)
+ yaffs_trace(YAFFS_TRACE_ERROR,
+ "gc: page in gc mismatch: %d %d %d %d",
+ old_chunk,
+ matching_chunk,
+ tags.obj_id,
+ tags.chunk_id);
+ }
+
+ if (!object) {
+ yaffs_trace(YAFFS_TRACE_ERROR,
+ "page %d in gc has no object: %d %d %d ",
+ old_chunk,
+ tags.obj_id, tags.chunk_id,
+ tags.n_bytes);
+ }
+
+ if (object &&
+ object->deleted &&
+ object->soft_del && tags.chunk_id != 0) {
+ /* Data chunk in a soft deleted file,
+ * throw it away.
+ * It's a soft deleted data chunk,
+ * No need to copy this, just forget
+ * about it and fix up the object.
+ */
+
+ /* Free chunks already includes
+ * softdeleted chunks, how ever this
+ * chunk is going to soon be really
+ * deleted which will increment free
+ * chunks. We have to decrement free
+ * chunks so this works out properly.
+ */
+ dev->n_free_chunks--;
+ bi->soft_del_pages--;
+
+ object->n_data_chunks--;
+ if (object->n_data_chunks <= 0) {
+ /* remeber to clean up obj */
+ dev->gc_cleanup_list[dev->n_clean_ups] = tags.obj_id;
+ dev->n_clean_ups++;
+ }
+ mark_flash = 0;
+ } else if (object) {
+ /* It's either a data chunk in a live
+ * file or an ObjectHeader, so we're
+ * interested in it.
+ * NB Need to keep the ObjectHeaders of
+ * deleted files until the whole file
+ * has been deleted off
+ */
+ tags.serial_number++;
+ dev->n_gc_copies++;
+
+ if (tags.chunk_id == 0) {
+ /* It is an object Id,
+ * We need to nuke the
+ * shrinkheader flags since its
+ * work is done.
+ * Also need to clean up
+ * shadowing.
+ */
+ struct yaffs_obj_hdr *oh;
+ oh = (struct yaffs_obj_hdr *) buffer;
+
+ oh->is_shrink = 0;
+ tags.extra_is_shrink = 0;
+ oh->shadows_obj = 0;
+ oh->inband_shadowed_obj_id = 0;
+ tags.extra_shadows = 0;
+
+ /* Update file size */
+ if (object->variant_type == YAFFS_OBJECT_TYPE_FILE) {
+ yaffs_oh_size_load(oh,
+ object->variant.file_variant.file_size);
+ tags.extra_file_size =
+ object->variant.file_variant.file_size;
+ }
+
+ yaffs_verify_oh(object, oh, &tags, 1);
+ new_chunk =
+ yaffs_write_new_chunk(dev, (u8 *) oh, &tags, 1);
+ } else {
+ new_chunk =
+ yaffs_write_new_chunk(dev, buffer, &tags, 1);
+ }
+
+ if (new_chunk < 0) {
+ ret_val = YAFFS_FAIL;
+ } else {
+
+ /* Now fix up the Tnodes etc. */
+
+ if (tags.chunk_id == 0) {
+ /* It's a header */
+ object->hdr_chunk = new_chunk;
+ object->serial = tags.serial_number;
+ } else {
+ /* It's a data chunk */
+ yaffs_put_chunk_in_file(object, tags.chunk_id,
+ new_chunk, 0);
+ }
+ }
+ }
+ if (ret_val == YAFFS_OK)
+ yaffs_chunk_del(dev, old_chunk, mark_flash, __LINE__);
+ return ret_val;
+}
+
+static int yaffs_gc_block(struct yaffs_dev *dev, int block, int whole_block)
+{
+ int old_chunk;
+ int ret_val = YAFFS_OK;
+ int i;
+ int is_checkpt_block;
+ int max_copies;
+ int chunks_before = yaffs_get_erased_chunks(dev);
+ int chunks_after;
+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, block);
+
+ is_checkpt_block = (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT);
+
+ yaffs_trace(YAFFS_TRACE_TRACING,
+ "Collecting block %d, in use %d, shrink %d, whole_block %d",
+ block, bi->pages_in_use, bi->has_shrink_hdr,
+ whole_block);
+
+ /*yaffs_verify_free_chunks(dev); */
+
+ if (bi->block_state == YAFFS_BLOCK_STATE_FULL)
+ bi->block_state = YAFFS_BLOCK_STATE_COLLECTING;
+
+ bi->has_shrink_hdr = 0; /* clear the flag so that the block can erase */
+
+ dev->gc_disable = 1;
+
+ yaffs_summary_gc(dev, block);
+
+ if (is_checkpt_block || !yaffs_still_some_chunks(dev, block)) {
+ yaffs_trace(YAFFS_TRACE_TRACING,
+ "Collecting block %d that has no chunks in use",
+ block);
+ yaffs_block_became_dirty(dev, block);
+ } else {
+
+ u8 *buffer = yaffs_get_temp_buffer(dev);
+
+ yaffs_verify_blk(dev, bi, block);
+
+ max_copies = (whole_block) ? dev->param.chunks_per_block : 5;
+ old_chunk = block * dev->param.chunks_per_block + dev->gc_chunk;
+
+ for (/* init already done */ ;
+ ret_val == YAFFS_OK &&
+ dev->gc_chunk < dev->param.chunks_per_block &&
+ (bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) &&
+ max_copies > 0;
+ dev->gc_chunk++, old_chunk++) {
+ if (yaffs_check_chunk_bit(dev, block, dev->gc_chunk)) {
+ /* Page is in use and might need to be copied */
+ max_copies--;
+ ret_val = yaffs_gc_process_chunk(dev, bi,
+ old_chunk, buffer);
+ }
+ }
+ yaffs_release_temp_buffer(dev, buffer);
+ }
+
+ yaffs_verify_collected_blk(dev, bi, block);
+
+ if (bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) {
+ /*
+ * The gc did not complete. Set block state back to FULL
+ * because checkpointing does not restore gc.
+ */
+ bi->block_state = YAFFS_BLOCK_STATE_FULL;
+ } else {
+ /* The gc completed. */
+ /* Do any required cleanups */
+ for (i = 0; i < dev->n_clean_ups; i++) {
+ /* Time to delete the file too */
+ struct yaffs_obj *object =
+ yaffs_find_by_number(dev, dev->gc_cleanup_list[i]);
+ if (object) {
+ yaffs_free_tnode(dev,
+ object->variant.file_variant.top);
+ object->variant.file_variant.top = NULL;
+ yaffs_trace(YAFFS_TRACE_GC,
+ "yaffs: About to finally delete object %d",
+ object->obj_id);
+ yaffs_generic_obj_del(object);
+ object->my_dev->n_deleted_files--;
+ }
+
+ }
+ chunks_after = yaffs_get_erased_chunks(dev);
+ if (chunks_before >= chunks_after)
+ yaffs_trace(YAFFS_TRACE_GC,
+ "gc did not increase free chunks before %d after %d",
+ chunks_before, chunks_after);
+ dev->gc_block = 0;
+ dev->gc_chunk = 0;
+ dev->n_clean_ups = 0;
+ }
+
+ dev->gc_disable = 0;
+
+ return ret_val;
+}
+
+/*
+ * find_gc_block() selects the dirtiest block (or close enough)
+ * for garbage collection.
+ */
+
+static unsigned yaffs_find_gc_block(struct yaffs_dev *dev,
+ int aggressive, int background)
+{
+ int i;
+ int iterations;
+ unsigned selected = 0;
+ int prioritised = 0;
+ int prioritised_exist = 0;
+ struct yaffs_block_info *bi;
+ int threshold;
+
+ /* First let's see if we need to grab a prioritised block */
+ if (dev->has_pending_prioritised_gc && !aggressive) {
+ dev->gc_dirtiest = 0;
+ bi = dev->block_info;
+ for (i = dev->internal_start_block;
+ i <= dev->internal_end_block && !selected; i++) {
+
+ if (bi->gc_prioritise) {
+ prioritised_exist = 1;
+ if (bi->block_state == YAFFS_BLOCK_STATE_FULL &&
+ yaffs_block_ok_for_gc(dev, bi)) {
+ selected = i;
+ prioritised = 1;
+ }
+ }
+ bi++;
+ }
+
+ /*
+ * If there is a prioritised block and none was selected then
+ * this happened because there is at least one old dirty block
+ * gumming up the works. Let's gc the oldest dirty block.
+ */
+
+ if (prioritised_exist &&
+ !selected && dev->oldest_dirty_block > 0)
+ selected = dev->oldest_dirty_block;
+
+ if (!prioritised_exist) /* None found, so we can clear this */
+ dev->has_pending_prioritised_gc = 0;
+ }
+
+ /* If we're doing aggressive GC then we are happy to take a less-dirty
+ * block, and search harder.
+ * else (leasurely gc), then we only bother to do this if the
+ * block has only a few pages in use.
+ */
+
+ if (!selected) {
+ int pages_used;
+ int n_blocks =
+ dev->internal_end_block - dev->internal_start_block + 1;
+ if (aggressive) {
+ threshold = dev->param.chunks_per_block;
+ iterations = n_blocks;
+ } else {
+ int max_threshold;
+
+ if (background)
+ max_threshold = dev->param.chunks_per_block / 2;
+ else
+ max_threshold = dev->param.chunks_per_block / 8;
+
+ if (max_threshold < YAFFS_GC_PASSIVE_THRESHOLD)
+ max_threshold = YAFFS_GC_PASSIVE_THRESHOLD;
+
+ threshold = background ? (dev->gc_not_done + 2) * 2 : 0;
+ if (threshold < YAFFS_GC_PASSIVE_THRESHOLD)
+ threshold = YAFFS_GC_PASSIVE_THRESHOLD;
+ if (threshold > max_threshold)
+ threshold = max_threshold;
+
+ iterations = n_blocks / 16 + 1;
+ if (iterations > 100)
+ iterations = 100;
+ }
+
+ for (i = 0;
+ i < iterations &&
+ (dev->gc_dirtiest < 1 ||
+ dev->gc_pages_in_use > YAFFS_GC_GOOD_ENOUGH);
+ i++) {
+ dev->gc_block_finder++;
+ if (dev->gc_block_finder < dev->internal_start_block ||
+ dev->gc_block_finder > dev->internal_end_block)
+ dev->gc_block_finder =
+ dev->internal_start_block;
+
+ bi = yaffs_get_block_info(dev, dev->gc_block_finder);
+
+ pages_used = bi->pages_in_use - bi->soft_del_pages;
+
+ if (bi->block_state == YAFFS_BLOCK_STATE_FULL &&
+ pages_used < dev->param.chunks_per_block &&
+ (dev->gc_dirtiest < 1 ||
+ pages_used < dev->gc_pages_in_use) &&
+ yaffs_block_ok_for_gc(dev, bi)) {
+ dev->gc_dirtiest = dev->gc_block_finder;
+ dev->gc_pages_in_use = pages_used;
+ }
+ }
+
+ if (dev->gc_dirtiest > 0 && dev->gc_pages_in_use <= threshold)
+ selected = dev->gc_dirtiest;
+ }
+
+ /*
+ * If nothing has been selected for a while, try the oldest dirty
+ * because that's gumming up the works.
+ */
+
+ if (!selected && dev->param.is_yaffs2 &&
+ dev->gc_not_done >= (background ? 10 : 20)) {
+ yaffs2_find_oldest_dirty_seq(dev);
+ if (dev->oldest_dirty_block > 0) {
+ selected = dev->oldest_dirty_block;
+ dev->gc_dirtiest = selected;
+ dev->oldest_dirty_gc_count++;
+ bi = yaffs_get_block_info(dev, selected);
+ dev->gc_pages_in_use =
+ bi->pages_in_use - bi->soft_del_pages;
+ } else {
+ dev->gc_not_done = 0;
+ }
+ }
+
+ if (selected) {
+ yaffs_trace(YAFFS_TRACE_GC,
+ "GC Selected block %d with %d free, prioritised:%d",
+ selected,
+ dev->param.chunks_per_block - dev->gc_pages_in_use,
+ prioritised);
+
+ dev->n_gc_blocks++;
+ if (background)
+ dev->bg_gcs++;
+
+ dev->gc_dirtiest = 0;
+ dev->gc_pages_in_use = 0;
+ dev->gc_not_done = 0;
+ if (dev->refresh_skip > 0)
+ dev->refresh_skip--;
+ } else {
+ dev->gc_not_done++;
+ yaffs_trace(YAFFS_TRACE_GC,
+ "GC none: finder %d skip %d threshold %d dirtiest %d using %d oldest %d%s",
+ dev->gc_block_finder, dev->gc_not_done, threshold,
+ dev->gc_dirtiest, dev->gc_pages_in_use,
+ dev->oldest_dirty_block, background ? " bg" : "");
+ }
+
+ return selected;
+}
+
+/* New garbage collector
+ * If we're very low on erased blocks then we do aggressive garbage collection
+ * otherwise we do "leasurely" garbage collection.
+ * Aggressive gc looks further (whole array) and will accept less dirty blocks.
+ * Passive gc only inspects smaller areas and only accepts more dirty blocks.
+ *
+ * The idea is to help clear out space in a more spread-out manner.
+ * Dunno if it really does anything useful.
+ */
+static int yaffs_check_gc(struct yaffs_dev *dev, int background)
+{
+ int aggressive = 0;
+ int gc_ok = YAFFS_OK;
+ int max_tries = 0;
+ int min_erased;
+ int erased_chunks;
+ int checkpt_block_adjust;
+
+ if (dev->param.gc_control && (dev->param.gc_control(dev) & 1) == 0)
+ return YAFFS_OK;
+
+ if (dev->gc_disable)
+ /* Bail out so we don't get recursive gc */
+ return YAFFS_OK;
+
+ /* This loop should pass the first time.
+ * Only loops here if the collection does not increase space.
+ */
+
+ do {
+ max_tries++;
+
+ checkpt_block_adjust = yaffs_calc_checkpt_blocks_required(dev);
+
+ min_erased =
+ dev->param.n_reserved_blocks + checkpt_block_adjust + 1;
+ erased_chunks =
+ dev->n_erased_blocks * dev->param.chunks_per_block;
+
+ /* If we need a block soon then do aggressive gc. */
+ if (dev->n_erased_blocks < min_erased)
+ aggressive = 1;
+ else {
+ if (!background
+ && erased_chunks > (dev->n_free_chunks / 4))
+ break;
+
+ if (dev->gc_skip > 20)
+ dev->gc_skip = 20;
+ if (erased_chunks < dev->n_free_chunks / 2 ||
+ dev->gc_skip < 1 || background)
+ aggressive = 0;
+ else {
+ dev->gc_skip--;
+ break;
+ }
+ }
+
+ dev->gc_skip = 5;
+
+ /* If we don't already have a block being gc'd then see if we
+ * should start another */
+
+ if (dev->gc_block < 1 && !aggressive) {
+ dev->gc_block = yaffs2_find_refresh_block(dev);
+ dev->gc_chunk = 0;
+ dev->n_clean_ups = 0;
+ }
+ if (dev->gc_block < 1) {
+ dev->gc_block =
+ yaffs_find_gc_block(dev, aggressive, background);
+ dev->gc_chunk = 0;
+ dev->n_clean_ups = 0;
+ }
+
+ if (dev->gc_block > 0) {
+ dev->all_gcs++;
+ if (!aggressive)
+ dev->passive_gc_count++;
+
+ yaffs_trace(YAFFS_TRACE_GC,
+ "yaffs: GC n_erased_blocks %d aggressive %d",
+ dev->n_erased_blocks, aggressive);
+
+ gc_ok = yaffs_gc_block(dev, dev->gc_block, aggressive);
+ }
+
+ if (dev->n_erased_blocks < (dev->param.n_reserved_blocks) &&
+ dev->gc_block > 0) {
+ yaffs_trace(YAFFS_TRACE_GC,
+ "yaffs: GC !!!no reclaim!!! n_erased_blocks %d after try %d block %d",
+ dev->n_erased_blocks, max_tries,
+ dev->gc_block);
+ }
+ } while ((dev->n_erased_blocks < dev->param.n_reserved_blocks) &&
+ (dev->gc_block > 0) && (max_tries < 2));
+
+ return aggressive ? gc_ok : YAFFS_OK;
+}
+
+/*
+ * yaffs_bg_gc()
+ * Garbage collects. Intended to be called from a background thread.
+ * Returns non-zero if at least half the free chunks are erased.
+ */
+int yaffs_bg_gc(struct yaffs_dev *dev, unsigned urgency)
+{
+ int erased_chunks = dev->n_erased_blocks * dev->param.chunks_per_block;
+
+ yaffs_trace(YAFFS_TRACE_BACKGROUND, "Background gc %u", urgency);
+
+ yaffs_check_gc(dev, 1);
+ return erased_chunks > dev->n_free_chunks / 2;
+}
+
+/*-------------------- Data file manipulation -----------------*/
+
+static int yaffs_rd_data_obj(struct yaffs_obj *in, int inode_chunk, u8 * buffer)
+{
+ int nand_chunk = yaffs_find_chunk_in_file(in, inode_chunk, NULL);
+
+ if (nand_chunk >= 0)
+ return yaffs_rd_chunk_tags_nand(in->my_dev, nand_chunk,
+ buffer, NULL);
+ else {
+ yaffs_trace(YAFFS_TRACE_NANDACCESS,
+ "Chunk %d not found zero instead",
+ nand_chunk);
+ /* get sane (zero) data if you read a hole */
+ memset(buffer, 0, in->my_dev->data_bytes_per_chunk);
+ return 0;
+ }
+
+}
+
+void yaffs_chunk_del(struct yaffs_dev *dev, int chunk_id, int mark_flash,
+ int lyn)
+{
+ int block;
+ int page;
+ struct yaffs_ext_tags tags;
+ struct yaffs_block_info *bi;
+
+ if (chunk_id <= 0)
+ return;
+
+ dev->n_deletions++;
+ block = chunk_id / dev->param.chunks_per_block;
+ page = chunk_id % dev->param.chunks_per_block;
+
+ if (!yaffs_check_chunk_bit(dev, block, page))
+ yaffs_trace(YAFFS_TRACE_VERIFY,
+ "Deleting invalid chunk %d", chunk_id);
+
+ bi = yaffs_get_block_info(dev, block);
+
+ yaffs2_update_oldest_dirty_seq(dev, block, bi);
+
+ yaffs_trace(YAFFS_TRACE_DELETION,
+ "line %d delete of chunk %d",
+ lyn, chunk_id);
+
+ if (!dev->param.is_yaffs2 && mark_flash &&
+ bi->block_state != YAFFS_BLOCK_STATE_COLLECTING) {
+
+ memset(&tags, 0, sizeof(tags));
+ tags.is_deleted = 1;
+ yaffs_wr_chunk_tags_nand(dev, chunk_id, NULL, &tags);
+ yaffs_handle_chunk_update(dev, chunk_id, &tags);
+ } else {
+ dev->n_unmarked_deletions++;
+ }
+
+ /* Pull out of the management area.
+ * If the whole block became dirty, this will kick off an erasure.
+ */
+ if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING ||
+ bi->block_state == YAFFS_BLOCK_STATE_FULL ||
+ bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN ||
+ bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) {
+ dev->n_free_chunks++;
+ yaffs_clear_chunk_bit(dev, block, page);
+ bi->pages_in_use--;
+
+ if (bi->pages_in_use == 0 &&
+ !bi->has_shrink_hdr &&
+ bi->block_state != YAFFS_BLOCK_STATE_ALLOCATING &&
+ bi->block_state != YAFFS_BLOCK_STATE_NEEDS_SCAN) {
+ yaffs_block_became_dirty(dev, block);
+ }
+ }
+}
+
+static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk,
+ const u8 *buffer, int n_bytes, int use_reserve)
+{
+ /* Find old chunk Need to do this to get serial number
+ * Write new one and patch into tree.
+ * Invalidate old tags.
+ */
+
+ int prev_chunk_id;
+ struct yaffs_ext_tags prev_tags;
+ int new_chunk_id;
+ struct yaffs_ext_tags new_tags;
+ struct yaffs_dev *dev = in->my_dev;
+
+ yaffs_check_gc(dev, 0);
+
+ /* Get the previous chunk at this location in the file if it exists.
+ * If it does not exist then put a zero into the tree. This creates
+ * the tnode now, rather than later when it is harder to clean up.
+ */
+ prev_chunk_id = yaffs_find_chunk_in_file(in, inode_chunk, &prev_tags);
+ if (prev_chunk_id < 1 &&
+ !yaffs_put_chunk_in_file(in, inode_chunk, 0, 0))
+ return 0;
+
+ /* Set up new tags */
+ memset(&new_tags, 0, sizeof(new_tags));
+
+ new_tags.chunk_id = inode_chunk;
+ new_tags.obj_id = in->obj_id;
+ new_tags.serial_number =
+ (prev_chunk_id > 0) ? prev_tags.serial_number + 1 : 1;
+ new_tags.n_bytes = n_bytes;
+
+ if (n_bytes < 1 || n_bytes > dev->param.total_bytes_per_chunk) {
+ yaffs_trace(YAFFS_TRACE_ERROR,
+ "Writing %d bytes to chunk!!!!!!!!!",
+ n_bytes);
+ BUG();
+ }
+
+ new_chunk_id =
+ yaffs_write_new_chunk(dev, buffer, &new_tags, use_reserve);
+
+ if (new_chunk_id > 0) {
+ yaffs_put_chunk_in_file(in, inode_chunk, new_chunk_id, 0);
+
+ if (prev_chunk_id > 0)
+ yaffs_chunk_del(dev, prev_chunk_id, 1, __LINE__);
+
+ yaffs_verify_file_sane(in);
+ }
+ return new_chunk_id;
+
+}
+
+
+
+static int yaffs_do_xattrib_mod(struct yaffs_obj *obj, int set,
+ const YCHAR *name, const void *value, int size,
+ int flags)
+{
+ struct yaffs_xattr_mod xmod;
+ int result;
+
+ xmod.set = set;
+ xmod.name = name;
+ xmod.data = value;
+ xmod.size = size;
+ xmod.flags = flags;
+ xmod.result = -ENOSPC;
+
+ result = yaffs_update_oh(obj, NULL, 0, 0, 0, &xmod);
+
+ if (result > 0)
+ return xmod.result;
+ else
+ return -ENOSPC;
+}
+
+static int yaffs_apply_xattrib_mod(struct yaffs_obj *obj, char *buffer,
+ struct yaffs_xattr_mod *xmod)
+{
+ int retval = 0;
+ int x_offs = sizeof(struct yaffs_obj_hdr);
+ struct yaffs_dev *dev = obj->my_dev;
+ int x_size = dev->data_bytes_per_chunk - sizeof(struct yaffs_obj_hdr);
+ char *x_buffer = buffer + x_offs;
+
+ if (xmod->set)
+ retval =
+ nval_set(x_buffer, x_size, xmod->name, xmod->data,
+ xmod->size, xmod->flags);
+ else
+ retval = nval_del(x_buffer, x_size, xmod->name);
+
+ obj->has_xattr = nval_hasvalues(x_buffer, x_size);
+ obj->xattr_known = 1;
+ xmod->result = retval;
+
+ return retval;
+}
+
+static int yaffs_do_xattrib_fetch(struct yaffs_obj *obj, const YCHAR *name,
+ void *value, int size)
+{
+ char *buffer = NULL;
+ int result;
+ struct yaffs_ext_tags tags;
+ struct yaffs_dev *dev = obj->my_dev;
+ int x_offs = sizeof(struct yaffs_obj_hdr);
+ int x_size = dev->data_bytes_per_chunk - sizeof(struct yaffs_obj_hdr);
+ char *x_buffer;
+ int retval = 0;
+
+ if (obj->hdr_chunk < 1)
+ return -ENODATA;
+
+ /* If we know that the object has no xattribs then don't do all the
+ * reading and parsing.
+ */
+ if (obj->xattr_known && !obj->has_xattr) {
+ if (name)
+ return -ENODATA;
+ else
+ return 0;
+ }
+
+ buffer = (char *)yaffs_get_temp_buffer(dev);
+ if (!buffer)
+ return -ENOMEM;
+
+ result =
+ yaffs_rd_chunk_tags_nand(dev, obj->hdr_chunk, (u8 *) buffer, &tags);
+
+ if (result != YAFFS_OK)
+ retval = -ENOENT;
+ else {
+ x_buffer = buffer + x_offs;
+
+ if (!obj->xattr_known) {
+ obj->has_xattr = nval_hasvalues(x_buffer, x_size);
+ obj->xattr_known = 1;
+ }
+
+ if (name)
+ retval = nval_get(x_buffer, x_size, name, value, size);
+ else
+ retval = nval_list(x_buffer, x_size, value, size);
+ }
+ yaffs_release_temp_buffer(dev, (u8 *) buffer);
+ return retval;
+}
+
+int yaffs_set_xattrib(struct yaffs_obj *obj, const YCHAR * name,
+ const void *value, int size, int flags)
+{
+ return yaffs_do_xattrib_mod(obj, 1, name, value, size, flags);
+}
+
+int yaffs_remove_xattrib(struct yaffs_obj *obj, const YCHAR * name)
+{
+ return yaffs_do_xattrib_mod(obj, 0, name, NULL, 0, 0);
+}
+
+int yaffs_get_xattrib(struct yaffs_obj *obj, const YCHAR * name, void *value,
+ int size)
+{
+ return yaffs_do_xattrib_fetch(obj, name, value, size);
+}
+
+int yaffs_list_xattrib(struct yaffs_obj *obj, char *buffer, int size)
+{
+ return yaffs_do_xattrib_fetch(obj, NULL, buffer, size);
+}
+
+static void yaffs_check_obj_details_loaded(struct yaffs_obj *in)
+{
+ u8 *buf;
+ struct yaffs_obj_hdr *oh;
+ struct yaffs_dev *dev;
+ struct yaffs_ext_tags tags;
+
+ if (!in || !in->lazy_loaded || in->hdr_chunk < 1)
+ return;
+
+ dev = in->my_dev;
+ in->lazy_loaded = 0;
+ buf = yaffs_get_temp_buffer(dev);
+
+ yaffs_rd_chunk_tags_nand(dev, in->hdr_chunk, buf, &tags);
+ oh = (struct yaffs_obj_hdr *)buf;
+
+ in->yst_mode = oh->yst_mode;
+ yaffs_load_attribs(in, oh);
+ yaffs_set_obj_name_from_oh(in, oh);
+
+ if (in->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) {
+ in->variant.symlink_variant.alias =
+ yaffs_clone_str(oh->alias);
+ }
+ yaffs_release_temp_buffer(dev, buf);
+}
+
+static void yaffs_load_name_from_oh(struct yaffs_dev *dev, YCHAR *name,
+ const YCHAR *oh_name, int buff_size)
+{
+#ifdef CONFIG_YAFFS_AUTO_UNICODE
+ if (dev->param.auto_unicode) {
+ if (*oh_name) {
+ /* It is an ASCII name, do an ASCII to
+ * unicode conversion */
+ const char *ascii_oh_name = (const char *)oh_name;
+ int n = buff_size - 1;
+ while (n > 0 && *ascii_oh_name) {
+ *name = *ascii_oh_name;
+ name++;
+ ascii_oh_name++;
+ n--;
+ }
+ } else {
+ yaffs_strncpy(name, oh_name + 1, buff_size - 1);
+ }
+
+ return;
+ }
+#endif
+
+ yaffs_strncpy(name, oh_name, buff_size - 1);
+}
+
+static void yaffs_load_oh_from_name(struct yaffs_dev *dev, YCHAR *oh_name,
+ const YCHAR *name)
+{
+#ifdef CONFIG_YAFFS_AUTO_UNICODE
+ int is_ascii;
+ YCHAR *w;
+
+ if (dev->param.auto_unicode) {
+
+ is_ascii = 1;
+ w = name;
+
+ /* Figure out if the name will fit in ascii character set */
+ while (is_ascii && *w) {
+ if ((*w) & 0xff00)
+ is_ascii = 0;
+ w++;
+ }
+
+ if (is_ascii) {
+ /* It is an ASCII name, so convert unicode to ascii */
+ char *ascii_oh_name = (char *)oh_name;
+ int n = YAFFS_MAX_NAME_LENGTH - 1;
+ while (n > 0 && *name) {
+ *ascii_oh_name = *name;
+ name++;
+ ascii_oh_name++;
+ n--;
+ }
+ } else {
+ /* Unicode name, so save starting at the second YCHAR */
+ *oh_name = 0;
+ yaffs_strncpy(oh_name + 1, name, YAFFS_MAX_NAME_LENGTH - 2);
+ }
+
+ return;
+ }
+#endif
+
+ yaffs_strncpy(oh_name, name, YAFFS_MAX_NAME_LENGTH - 1);
+}
+
+/* UpdateObjectHeader updates the header on NAND for an object.
+ * If name is not NULL, then that new name is used.
+ */
+int yaffs_update_oh(struct yaffs_obj *in, const YCHAR *name, int force,
+ int is_shrink, int shadows, struct yaffs_xattr_mod *xmod)
+{
+
+ struct yaffs_block_info *bi;
+ struct yaffs_dev *dev = in->my_dev;
+ int prev_chunk_id;
+ int ret_val = 0;
+ int new_chunk_id;
+ struct yaffs_ext_tags new_tags;
+ struct yaffs_ext_tags old_tags;
+ const YCHAR *alias = NULL;
+ u8 *buffer = NULL;
+ YCHAR old_name[YAFFS_MAX_NAME_LENGTH + 1];
+ struct yaffs_obj_hdr *oh = NULL;
+ loff_t file_size = 0;
+
+ yaffs_strcpy(old_name, _Y("silly old name"));
+
+ if (in->fake && in != dev->root_dir && !force && !xmod)
+ return ret_val;
+
+ yaffs_check_gc(dev, 0);
+ yaffs_check_obj_details_loaded(in);
+
+ buffer = yaffs_get_temp_buffer(in->my_dev);
+ oh = (struct yaffs_obj_hdr *)buffer;
+
+ prev_chunk_id = in->hdr_chunk;
+
+ if (prev_chunk_id > 0) {
+ yaffs_rd_chunk_tags_nand(dev, prev_chunk_id,
+ buffer, &old_tags);
+
+ yaffs_verify_oh(in, oh, &old_tags, 0);
+ memcpy(old_name, oh->name, sizeof(oh->name));
+ memset(buffer, 0xff, sizeof(struct yaffs_obj_hdr));
+ } else {
+ memset(buffer, 0xff, dev->data_bytes_per_chunk);
+ }
+
+ oh->type = in->variant_type;
+ oh->yst_mode = in->yst_mode;
+ oh->shadows_obj = oh->inband_shadowed_obj_id = shadows;
+
+ yaffs_load_attribs_oh(oh, in);
+
+ if (in->parent)
+ oh->parent_obj_id = in->parent->obj_id;
+ else
+ oh->parent_obj_id = 0;
+
+ if (name && *name) {
+ memset(oh->name, 0, sizeof(oh->name));
+ yaffs_load_oh_from_name(dev, oh->name, name);
+ } else if (prev_chunk_id > 0) {
+ memcpy(oh->name, old_name, sizeof(oh->name));
+ } else {
+ memset(oh->name, 0, sizeof(oh->name));
+ }
+
+ oh->is_shrink = is_shrink;
+
+ switch (in->variant_type) {
+ case YAFFS_OBJECT_TYPE_UNKNOWN:
+ /* Should not happen */
+ break;
+ case YAFFS_OBJECT_TYPE_FILE:
+ if (oh->parent_obj_id != YAFFS_OBJECTID_DELETED &&
+ oh->parent_obj_id != YAFFS_OBJECTID_UNLINKED)
+ file_size = in->variant.file_variant.file_size;
+ yaffs_oh_size_load(oh, file_size);
+ break;
+ case YAFFS_OBJECT_TYPE_HARDLINK:
+ oh->equiv_id = in->variant.hardlink_variant.equiv_id;
+ break;
+ case YAFFS_OBJECT_TYPE_SPECIAL:
+ /* Do nothing */
+ break;
+ case YAFFS_OBJECT_TYPE_DIRECTORY:
+ /* Do nothing */
+ break;
+ case YAFFS_OBJECT_TYPE_SYMLINK:
+ alias = in->variant.symlink_variant.alias;
+ if (!alias)
+ alias = _Y("no alias");
+ yaffs_strncpy(oh->alias, alias, YAFFS_MAX_ALIAS_LENGTH);
+ oh->alias[YAFFS_MAX_ALIAS_LENGTH] = 0;
+ break;
+ }
+
+ /* process any xattrib modifications */
+ if (xmod)
+ yaffs_apply_xattrib_mod(in, (char *)buffer, xmod);
+
+ /* Tags */
+ memset(&new_tags, 0, sizeof(new_tags));
+ in->serial++;
+ new_tags.chunk_id = 0;
+ new_tags.obj_id = in->obj_id;
+ new_tags.serial_number = in->serial;
+
+ /* Add extra info for file header */
+ new_tags.extra_available = 1;
+ new_tags.extra_parent_id = oh->parent_obj_id;
+ new_tags.extra_file_size = file_size;
+ new_tags.extra_is_shrink = oh->is_shrink;
+ new_tags.extra_equiv_id = oh->equiv_id;
+ new_tags.extra_shadows = (oh->shadows_obj > 0) ? 1 : 0;
+ new_tags.extra_obj_type = in->variant_type;
+ yaffs_verify_oh(in, oh, &new_tags, 1);
+
+ /* Create new chunk in NAND */
+ new_chunk_id =
+ yaffs_write_new_chunk(dev, buffer, &new_tags,
+ (prev_chunk_id > 0) ? 1 : 0);
+
+ if (buffer)
+ yaffs_release_temp_buffer(dev, buffer);
+
+ if (new_chunk_id < 0)
+ return new_chunk_id;
+
+ in->hdr_chunk = new_chunk_id;
+
+ if (prev_chunk_id > 0)
+ yaffs_chunk_del(dev, prev_chunk_id, 1, __LINE__);
+
+ if (!yaffs_obj_cache_dirty(in))
+ in->dirty = 0;
+
+ /* If this was a shrink, then mark the block
+ * that the chunk lives on */
+ if (is_shrink) {
+ bi = yaffs_get_block_info(in->my_dev,
+ new_chunk_id /
+ in->my_dev->param.chunks_per_block);
+ bi->has_shrink_hdr = 1;
+ }
+
+
+ return new_chunk_id;
+}
+
+/*--------------------- File read/write ------------------------
+ * Read and write have very similar structures.
+ * In general the read/write has three parts to it
+ * An incomplete chunk to start with (if the read/write is not chunk-aligned)
+ * Some complete chunks
+ * An incomplete chunk to end off with
+ *
+ * Curve-balls: the first chunk might also be the last chunk.
+ */
+
+int yaffs_file_rd(struct yaffs_obj *in, u8 * buffer, loff_t offset, int n_bytes)
+{
+ int chunk;
+ u32 start;
+ int n_copy;
+ int n = n_bytes;
+ int n_done = 0;
+ struct yaffs_cache *cache;
+ struct yaffs_dev *dev;
+
+ dev = in->my_dev;
+
+ while (n > 0) {
+ yaffs_addr_to_chunk(dev, offset, &chunk, &start);
+ chunk++;
+
+ /* OK now check for the curveball where the start and end are in
+ * the same chunk.
+ */
+ if ((start + n) < dev->data_bytes_per_chunk)
+ n_copy = n;
+ else
+ n_copy = dev->data_bytes_per_chunk - start;
+
+ cache = yaffs_find_chunk_cache(in, chunk);
+
+ /* If the chunk is already in the cache or it is less than
+ * a whole chunk or we're using inband tags then use the cache
+ * (if there is caching) else bypass the cache.
+ */
+ if (cache || n_copy != dev->data_bytes_per_chunk ||
+ dev->param.inband_tags) {
+ if (dev->param.n_caches > 0) {
+
+ /* If we can't find the data in the cache,
+ * then load it up. */
+
+ if (!cache) {
+ cache =
+ yaffs_grab_chunk_cache(in->my_dev);
+ cache->object = in;
+ cache->chunk_id = chunk;
+ cache->dirty = 0;
+ cache->locked = 0;
+ yaffs_rd_data_obj(in, chunk,
+ cache->data);
+ cache->n_bytes = 0;
+ }
+
+ yaffs_use_cache(dev, cache, 0);
+
+ cache->locked = 1;
+
+ memcpy(buffer, &cache->data[start], n_copy);
+
+ cache->locked = 0;
+ } else {
+ /* Read into the local buffer then copy.. */
+
+ u8 *local_buffer =
+ yaffs_get_temp_buffer(dev);
+ yaffs_rd_data_obj(in, chunk, local_buffer);
+
+ memcpy(buffer, &local_buffer[start], n_copy);
+
+ yaffs_release_temp_buffer(dev, local_buffer);
+ }
+ } else {
+ /* A full chunk. Read directly into the buffer. */
+ yaffs_rd_data_obj(in, chunk, buffer);
+ }
+ n -= n_copy;
+ offset += n_copy;
+ buffer += n_copy;
+ n_done += n_copy;
+ }
+ return n_done;
+}
+
+int yaffs_do_file_wr(struct yaffs_obj *in, const u8 *buffer, loff_t offset,
+ int n_bytes, int write_through)
+{
+
+ int chunk;
+ u32 start;
+ int n_copy;
+ int n = n_bytes;
+ int n_done = 0;
+ int n_writeback;
+ loff_t start_write = offset;
+ int chunk_written = 0;
+ u32 n_bytes_read;
+ loff_t chunk_start;
+ struct yaffs_dev *dev;
+
+ dev = in->my_dev;
+
+ while (n > 0 && chunk_written >= 0) {
+ yaffs_addr_to_chunk(dev, offset, &chunk, &start);
+
+ if (((loff_t)chunk) *
+ dev->data_bytes_per_chunk + start != offset ||
+ start >= dev->data_bytes_per_chunk) {
+ yaffs_trace(YAFFS_TRACE_ERROR,
+ "AddrToChunk of offset %lld gives chunk %d start %d",
+ offset, chunk, start);
+ }
+ chunk++; /* File pos to chunk in file offset */
+
+ /* OK now check for the curveball where the start and end are in
+ * the same chunk.
+ */
+
+ if ((start + n) < dev->data_bytes_per_chunk) {
+ n_copy = n;
+
+ /* Now calculate how many bytes to write back....
+ * If we're overwriting and not writing to then end of
+ * file then we need to write back as much as was there
+ * before.
+ */
+
+ chunk_start = (((loff_t)(chunk - 1)) *
+ dev->data_bytes_per_chunk);
+
+ if (chunk_start > in->variant.file_variant.file_size)
+ n_bytes_read = 0; /* Past end of file */
+ else
+ n_bytes_read =
+ in->variant.file_variant.file_size -
+ chunk_start;
+
+ if (n_bytes_read > dev->data_bytes_per_chunk)
+ n_bytes_read = dev->data_bytes_per_chunk;
+
+ n_writeback =
+ (n_bytes_read >
+ (start + n)) ? n_bytes_read : (start + n);
+
+ if (n_writeback < 0 ||
+ n_writeback > dev->data_bytes_per_chunk)
+ BUG();
+
+ } else {
+ n_copy = dev->data_bytes_per_chunk - start;
+ n_writeback = dev->data_bytes_per_chunk;
+ }
+
+ if (n_copy != dev->data_bytes_per_chunk ||
+ dev->param.inband_tags) {
+ /* An incomplete start or end chunk (or maybe both
+ * start and end chunk), or we're using inband tags,
+ * so we want to use the cache buffers.
+ */
+ if (dev->param.n_caches > 0) {
+ struct yaffs_cache *cache;
+
+ /* If we can't find the data in the cache, then
+ * load the cache */
+ cache = yaffs_find_chunk_cache(in, chunk);
+
+ if (!cache &&
+ yaffs_check_alloc_available(dev, 1)) {
+ cache = yaffs_grab_chunk_cache(dev);
+ cache->object = in;
+ cache->chunk_id = chunk;
+ cache->dirty = 0;
+ cache->locked = 0;
+ yaffs_rd_data_obj(in, chunk,
+ cache->data);
+ } else if (cache &&
+ !cache->dirty &&
+ !yaffs_check_alloc_available(dev,
+ 1)) {
+ /* Drop the cache if it was a read cache
+ * item and no space check has been made
+ * for it.
+ */
+ cache = NULL;
+ }
+
+ if (cache) {
+ yaffs_use_cache(dev, cache, 1);
+ cache->locked = 1;
+
+ memcpy(&cache->data[start], buffer,
+ n_copy);
+
+ cache->locked = 0;
+ cache->n_bytes = n_writeback;
+
+ if (write_through) {
+ chunk_written =
+ yaffs_wr_data_obj
+ (cache->object,
+ cache->chunk_id,
+ cache->data,
+ cache->n_bytes, 1);
+ cache->dirty = 0;
+ }
+ } else {
+ chunk_written = -1; /* fail write */
+ }
+ } else {
+ /* An incomplete start or end chunk (or maybe
+ * both start and end chunk). Read into the
+ * local buffer then copy over and write back.
+ */
+
+ u8 *local_buffer = yaffs_get_temp_buffer(dev);
+
+ yaffs_rd_data_obj(in, chunk, local_buffer);
+ memcpy(&local_buffer[start], buffer, n_copy);
+
+ chunk_written =
+ yaffs_wr_data_obj(in, chunk,
+ local_buffer,
+ n_writeback, 0);
+
+ yaffs_release_temp_buffer(dev, local_buffer);
+ }
+ } else {
+ /* A full chunk. Write directly from the buffer. */
+
+ chunk_written =
+ yaffs_wr_data_obj(in, chunk, buffer,
+ dev->data_bytes_per_chunk, 0);
+
+ /* Since we've overwritten the cached data,
+ * we better invalidate it. */
+ yaffs_invalidate_chunk_cache(in, chunk);
+ }
+
+ if (chunk_written >= 0) {
+ n -= n_copy;
+ offset += n_copy;
+ buffer += n_copy;
+ n_done += n_copy;
+ }
+ }
+
+ /* Update file object */
+
+ if ((start_write + n_done) > in->variant.file_variant.file_size)
+ in->variant.file_variant.file_size = (start_write + n_done);
+
+ in->dirty = 1;
+ return n_done;
+}
+
+int yaffs_wr_file(struct yaffs_obj *in, const u8 *buffer, loff_t offset,
+ int n_bytes, int write_through)
+{
+ yaffs2_handle_hole(in, offset);
+ return yaffs_do_file_wr(in, buffer, offset, n_bytes, write_through);
+}
+
+/* ---------------------- File resizing stuff ------------------ */
+
+static void yaffs_prune_chunks(struct yaffs_obj *in, loff_t new_size)
+{
+
+ struct yaffs_dev *dev = in->my_dev;
+ loff_t old_size = in->variant.file_variant.file_size;
+ int i;
+ int chunk_id;
+ u32 dummy;
+ int last_del;
+ int start_del;
+
+ if (old_size > 0)
+ yaffs_addr_to_chunk(dev, old_size - 1, &last_del, &dummy);
+ else
+ last_del = 0;
+
+ yaffs_addr_to_chunk(dev, new_size + dev->data_bytes_per_chunk - 1,
+ &start_del, &dummy);
+ last_del++;
+ start_del++;
+
+ /* Delete backwards so that we don't end up with holes if
+ * power is lost part-way through the operation.
+ */
+ for (i = last_del; i >= start_del; i--) {
+ /* NB this could be optimised somewhat,
+ * eg. could retrieve the tags and write them without
+ * using yaffs_chunk_del
+ */
+
+ chunk_id = yaffs_find_del_file_chunk(in, i, NULL);
+
+ if (chunk_id < 1)
+ continue;
+
+ if (chunk_id <
+ (dev->internal_start_block * dev->param.chunks_per_block) ||
+ chunk_id >=
+ ((dev->internal_end_block + 1) *
+ dev->param.chunks_per_block)) {
+ yaffs_trace(YAFFS_TRACE_ALWAYS,
+ "Found daft chunk_id %d for %d",
+ chunk_id, i);
+ } else {
+ in->n_data_chunks--;
+ yaffs_chunk_del(dev, chunk_id, 1, __LINE__);
+ }
+ }
+}
+
+void yaffs_resize_file_down(struct yaffs_obj *obj, loff_t new_size)
+{
+ int new_full;
+ u32 new_partial;
+ struct yaffs_dev *dev = obj->my_dev;
+
+ yaffs_addr_to_chunk(dev, new_size, &new_full, &new_partial);
+
+ yaffs_prune_chunks(obj, new_size);
+
+ if (new_partial != 0) {
+ int last_chunk = 1 + new_full;
+ u8 *local_buffer = yaffs_get_temp_buffer(dev);
+
+ /* Rewrite the last chunk with its new size and zero pad */
+ yaffs_rd_data_obj(obj, last_chunk, local_buffer);
+ memset(local_buffer + new_partial, 0,
+ dev->data_bytes_per_chunk - new_partial);
+
+ yaffs_wr_data_obj(obj, last_chunk, local_buffer,
+ new_partial, 1);
+
+ yaffs_release_temp_buffer(dev, local_buffer);
+ }
+
+ obj->variant.file_variant.file_size = new_size;
+
+ yaffs_prune_tree(dev, &obj->variant.file_variant);
+}
+
+int yaffs_resize_file(struct yaffs_obj *in, loff_t new_size)
+{
+ struct yaffs_dev *dev = in->my_dev;
+ loff_t old_size = in->variant.file_variant.file_size;
+
+ yaffs_flush_file_cache(in);
+ yaffs_invalidate_whole_cache(in);
+
+ yaffs_check_gc(dev, 0);
+
+ if (in->variant_type != YAFFS_OBJECT_TYPE_FILE)
+ return YAFFS_FAIL;
+
+ if (new_size == old_size)
+ return YAFFS_OK;
+
+ if (new_size > old_size) {
+ yaffs2_handle_hole(in, new_size);
+ in->variant.file_variant.file_size = new_size;
+ } else {
+ /* new_size < old_size */
+ yaffs_resize_file_down(in, new_size);
+ }
+
+ /* Write a new object header to reflect the resize.
+ * show we've shrunk the file, if need be
+ * Do this only if the file is not in the deleted directories
+ * and is not shadowed.
+ */
+ if (in->parent &&
+ !in->is_shadowed &&
+ in->parent->obj_id != YAFFS_OBJECTID_UNLINKED &&
+ in->parent->obj_id != YAFFS_OBJECTID_DELETED)
+ yaffs_update_oh(in, NULL, 0, 0, 0, NULL);
+
+ return YAFFS_OK;
+}
+
+int yaffs_flush_file(struct yaffs_obj *in, int update_time, int data_sync)
+{
+ if (!in->dirty)
+ return YAFFS_OK;
+
+ yaffs_flush_file_cache(in);
+
+ if (data_sync)
+ return YAFFS_OK;
+
+ if (update_time)
+ yaffs_load_current_time(in, 0, 0);
+
+ return (yaffs_update_oh(in, NULL, 0, 0, 0, NULL) >= 0) ?
+ YAFFS_OK : YAFFS_FAIL;
+}
+
+
+/* yaffs_del_file deletes the whole file data
+ * and the inode associated with the file.
+ * It does not delete the links associated with the file.
+ */
+static int yaffs_unlink_file_if_needed(struct yaffs_obj *in)
+{
+ int ret_val;
+ int del_now = 0;
+ struct yaffs_dev *dev = in->my_dev;
+
+ if (!in->my_inode)
+ del_now = 1;
+
+ if (del_now) {
+ ret_val =
+ yaffs_change_obj_name(in, in->my_dev->del_dir,
+ _Y("deleted"), 0, 0);
+ yaffs_trace(YAFFS_TRACE_TRACING,
+ "yaffs: immediate deletion of file %d",
+ in->obj_id);
+ in->deleted = 1;
+ in->my_dev->n_deleted_files++;
+ if (dev->param.disable_soft_del || dev->param.is_yaffs2)
+ yaffs_resize_file(in, 0);
+ yaffs_soft_del_file(in);
+ } else {
+ ret_val =
+ yaffs_change_obj_name(in, in->my_dev->unlinked_dir,
+ _Y("unlinked"), 0, 0);
+ }
+ return ret_val;
+}
+
+int yaffs_del_file(struct yaffs_obj *in)
+{
+ int ret_val = YAFFS_OK;
+ int deleted; /* Need to cache value on stack if in is freed */
+ struct yaffs_dev *dev = in->my_dev;
+
+ if (dev->param.disable_soft_del || dev->param.is_yaffs2)
+ yaffs_resize_file(in, 0);
+
+ if (in->n_data_chunks > 0) {
+ /* Use soft deletion if there is data in the file.
+ * That won't be the case if it has been resized to zero.
+ */
+ if (!in->unlinked)
+ ret_val = yaffs_unlink_file_if_needed(in);
+
+ deleted = in->deleted;
+
+ if (ret_val == YAFFS_OK && in->unlinked && !in->deleted) {
+ in->deleted = 1;
+ deleted = 1;
+ in->my_dev->n_deleted_files++;
+ yaffs_soft_del_file(in);
+ }
+ return deleted ? YAFFS_OK : YAFFS_FAIL;
+ } else {
+ /* The file has no data chunks so we toss it immediately */
+ yaffs_free_tnode(in->my_dev, in->variant.file_variant.top);
+ in->variant.file_variant.top = NULL;
+ yaffs_generic_obj_del(in);
+
+ return YAFFS_OK;
+ }
+}
+
+int yaffs_is_non_empty_dir(struct yaffs_obj *obj)
+{
+ return (obj &&
+ obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) &&
+ !(list_empty(&obj->variant.dir_variant.children));
+}
+
+static int yaffs_del_dir(struct yaffs_obj *obj)
+{
+ /* First check that the directory is empty. */
+ if (yaffs_is_non_empty_dir(obj))
+ return YAFFS_FAIL;
+
+ return yaffs_generic_obj_del(obj);
+}
+
+static int yaffs_del_symlink(struct yaffs_obj *in)
+{
+ kfree(in->variant.symlink_variant.alias);
+ in->variant.symlink_variant.alias = NULL;
+
+ return yaffs_generic_obj_del(in);
+}
+
+static int yaffs_del_link(struct yaffs_obj *in)
+{
+ /* remove this hardlink from the list associated with the equivalent
+ * object
+ */
+ list_del_init(&in->hard_links);
+ return yaffs_generic_obj_del(in);
+}
+
+int yaffs_del_obj(struct yaffs_obj *obj)
+{
+ int ret_val = -1;
+
+ switch (obj->variant_type) {
+ case YAFFS_OBJECT_TYPE_FILE:
+ ret_val = yaffs_del_file(obj);
+ break;
+ case YAFFS_OBJECT_TYPE_DIRECTORY:
+ if (!list_empty(&obj->variant.dir_variant.dirty)) {
+ yaffs_trace(YAFFS_TRACE_BACKGROUND,
+ "Remove object %d from dirty directories",
+ obj->obj_id);
+ list_del_init(&obj->variant.dir_variant.dirty);
+ }
+ return yaffs_del_dir(obj);
+ break;
+ case YAFFS_OBJECT_TYPE_SYMLINK:
+ ret_val = yaffs_del_symlink(obj);
+ break;
+ case YAFFS_OBJECT_TYPE_HARDLINK:
+ ret_val = yaffs_del_link(obj);
+ break;
+ case YAFFS_OBJECT_TYPE_SPECIAL:
+ ret_val = yaffs_generic_obj_del(obj);
+ break;
+ case YAFFS_OBJECT_TYPE_UNKNOWN:
+ ret_val = 0;
+ break; /* should not happen. */
+ }
+ return ret_val;
+}
+
+static int yaffs_unlink_worker(struct yaffs_obj *obj)
+{
+ int del_now = 0;
+
+ if (!obj)
+ return YAFFS_FAIL;
+
+ if (!obj->my_inode)
+ del_now = 1;
+
+ yaffs_update_parent(obj->parent);
+
+ if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) {
+ return yaffs_del_link(obj);
+ } else if (!list_empty(&obj->hard_links)) {
+ /* Curve ball: We're unlinking an object that has a hardlink.
+ *
+ * This problem arises because we are not strictly following
+ * The Linux link/inode model.
+ *
+ * We can't really delete the object.
+ * Instead, we do the following:
+ * - Select a hardlink.
+ * - Unhook it from the hard links
+ * - Move it from its parent directory so that the rename works.
+ * - Rename the object to the hardlink's name.
+ * - Delete the hardlink
+ */
+
+ struct yaffs_obj *hl;
+ struct yaffs_obj *parent;
+ int ret_val;
+ YCHAR name[YAFFS_MAX_NAME_LENGTH + 1];
+
+ hl = list_entry(obj->hard_links.next, struct yaffs_obj,
+ hard_links);
+
+ yaffs_get_obj_name(hl, name, YAFFS_MAX_NAME_LENGTH + 1);
+ parent = hl->parent;
+
+ list_del_init(&hl->hard_links);
+
+ yaffs_add_obj_to_dir(obj->my_dev->unlinked_dir, hl);
+
+ ret_val = yaffs_change_obj_name(obj, parent, name, 0, 0);
+
+ if (ret_val == YAFFS_OK)
+ ret_val = yaffs_generic_obj_del(hl);
+
+ return ret_val;
+
+ } else if (del_now) {
+ switch (obj->variant_type) {
+ case YAFFS_OBJECT_TYPE_FILE:
+ return yaffs_del_file(obj);
+ break;
+ case YAFFS_OBJECT_TYPE_DIRECTORY:
+ list_del_init(&obj->variant.dir_variant.dirty);
+ return yaffs_del_dir(obj);
+ break;
+ case YAFFS_OBJECT_TYPE_SYMLINK:
+ return yaffs_del_symlink(obj);
+ break;
+ case YAFFS_OBJECT_TYPE_SPECIAL:
+ return yaffs_generic_obj_del(obj);
+ break;
+ case YAFFS_OBJECT_TYPE_HARDLINK:
+ case YAFFS_OBJECT_TYPE_UNKNOWN:
+ default:
+ return YAFFS_FAIL;
+ }
+ } else if (yaffs_is_non_empty_dir(obj)) {
+ return YAFFS_FAIL;
+ } else {
+ return yaffs_change_obj_name(obj, obj->my_dev->unlinked_dir,
+ _Y("unlinked"), 0, 0);
+ }
+}
+
+static int yaffs_unlink_obj(struct yaffs_obj *obj)
+{
+ if (obj && obj->unlink_allowed)
+ return yaffs_unlink_worker(obj);
+
+ return YAFFS_FAIL;
+}
+
+int yaffs_unlinker(struct yaffs_obj *dir, const YCHAR *name)
+{
+ struct yaffs_obj *obj;
+
+ obj = yaffs_find_by_name(dir, name);
+ return yaffs_unlink_obj(obj);
+}
+
+/* Note:
+ * If old_name is NULL then we take old_dir as the object to be renamed.
+ */
+int yaffs_rename_obj(struct yaffs_obj *old_dir, const YCHAR *old_name,
+ struct yaffs_obj *new_dir, const YCHAR *new_name)
+{
+ struct yaffs_obj *obj = NULL;
+ struct yaffs_obj *existing_target = NULL;
+ int force = 0;
+ int result;
+ struct yaffs_dev *dev;
+
+ if (!old_dir || old_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {
+ BUG();
+ return YAFFS_FAIL;
+ }
+ if (!new_dir || new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {
+ BUG();
+ return YAFFS_FAIL;
+ }
+
+ dev = old_dir->my_dev;
+
+#ifdef CONFIG_YAFFS_CASE_INSENSITIVE
+ /* Special case for case insemsitive systems.
+ * While look-up is case insensitive, the name isn't.
+ * Therefore we might want to change x.txt to X.txt
+ */
+ if (old_dir == new_dir &&
+ old_name && new_name &&
+ yaffs_strcmp(old_name, new_name) == 0)
+ force = 1;
+#endif
+
+ if (yaffs_strnlen(new_name, YAFFS_MAX_NAME_LENGTH + 1) >
+ YAFFS_MAX_NAME_LENGTH)
+ /* ENAMETOOLONG */
+ return YAFFS_FAIL;
+
+ if (old_name)
+ obj = yaffs_find_by_name(old_dir, old_name);
+ else{
+ obj = old_dir;
+ old_dir = obj->parent;
+ }
+
+ if (obj && obj->rename_allowed) {
+ /* Now handle an existing target, if there is one */
+ existing_target = yaffs_find_by_name(new_dir, new_name);
+ if (yaffs_is_non_empty_dir(existing_target)) {
+ return YAFFS_FAIL; /* ENOTEMPTY */
+ } else if (existing_target && existing_target != obj) {
+ /* Nuke the target first, using shadowing,
+ * but only if it isn't the same object.
+ *
+ * Note we must disable gc here otherwise it can mess
+ * up the shadowing.
+ *
+ */
+ dev->gc_disable = 1;
+ yaffs_change_obj_name(obj, new_dir, new_name, force,
+ existing_target->obj_id);
+ existing_target->is_shadowed = 1;
+ yaffs_unlink_obj(existing_target);
+ dev->gc_disable = 0;
+ }
+
+ result = yaffs_change_obj_name(obj, new_dir, new_name, 1, 0);
+
+ yaffs_update_parent(old_dir);
+ if (new_dir != old_dir)
+ yaffs_update_parent(new_dir);
+
+ return result;
+ }
+ return YAFFS_FAIL;
+}
+
+/*----------------------- Initialisation Scanning ---------------------- */
+
+void yaffs_handle_shadowed_obj(struct yaffs_dev *dev, int obj_id,
+ int backward_scanning)
+{
+ struct yaffs_obj *obj;
+
+ if (backward_scanning) {
+ /* Handle YAFFS2 case (backward scanning)
+ * If the shadowed object exists then ignore.
+ */
+ obj = yaffs_find_by_number(dev, obj_id);
+ if (obj)
+ return;
+ }
+
+ /* Let's create it (if it does not exist) assuming it is a file so that
+ * it can do shrinking etc.
+ * We put it in unlinked dir to be cleaned up after the scanning
+ */
+ obj =
+ yaffs_find_or_create_by_number(dev, obj_id, YAFFS_OBJECT_TYPE_FILE);
+ if (!obj)
+ return;
+ obj->is_shadowed = 1;
+ yaffs_add_obj_to_dir(dev->unlinked_dir, obj);
+ obj->variant.file_variant.shrink_size = 0;
+ obj->valid = 1; /* So that we don't read any other info. */
+}
+
+void yaffs_link_fixup(struct yaffs_dev *dev, struct list_head *hard_list)
+{
+ struct list_head *lh;
+ struct list_head *save;
+ struct yaffs_obj *hl;
+ struct yaffs_obj *in;
+
+ list_for_each_safe(lh, save, hard_list) {
+ hl = list_entry(lh, struct yaffs_obj, hard_links);
+ in = yaffs_find_by_number(dev,
+ hl->variant.hardlink_variant.equiv_id);
+
+ if (in) {
+ /* Add the hardlink pointers */
+ hl->variant.hardlink_variant.equiv_obj = in;
+ list_add(&hl->hard_links, &in->hard_links);
+ } else {
+ /* Todo Need to report/handle this better.
+ * Got a problem... hardlink to a non-existant object
+ */
+ hl->variant.hardlink_variant.equiv_obj = NULL;
+ INIT_LIST_HEAD(&hl->hard_links);
+ }
+ }
+}
+
+static void yaffs_strip_deleted_objs(struct yaffs_dev *dev)
+{
+ /*
+ * Sort out state of unlinked and deleted objects after scanning.
+ */
+ struct list_head *i;
+ struct list_head *n;
+ struct yaffs_obj *l;
+
+ if (dev->read_only)
+ return;
+
+ /* Soft delete all the unlinked files */
+ list_for_each_safe(i, n,
+ &dev->unlinked_dir->variant.dir_variant.children) {
+ l = list_entry(i, struct yaffs_obj, siblings);
+ yaffs_del_obj(l);
+ }
+
+ list_for_each_safe(i, n, &dev->del_dir->variant.dir_variant.children) {
+ l = list_entry(i, struct yaffs_obj, siblings);
+ yaffs_del_obj(l);
+ }
+}
+
+/*
+ * This code iterates through all the objects making sure that they are rooted.
+ * Any unrooted objects are re-rooted in lost+found.
+ * An object needs to be in one of:
+ * - Directly under deleted, unlinked
+ * - Directly or indirectly under root.
+ *
+ * Note:
+ * This code assumes that we don't ever change the current relationships
+ * between directories:
+ * root_dir->parent == unlinked_dir->parent == del_dir->parent == NULL
+ * lost-n-found->parent == root_dir
+ *
+ * This fixes the problem where directories might have inadvertently been
+ * deleted leaving the object "hanging" without being rooted in the
+ * directory tree.
+ */
+
+static int yaffs_has_null_parent(struct yaffs_dev *dev, struct yaffs_obj *obj)
+{
+ return (obj == dev->del_dir ||
+ obj == dev->unlinked_dir || obj == dev->root_dir);
+}
+
+static void yaffs_fix_hanging_objs(struct yaffs_dev *dev)
+{
+ struct yaffs_obj *obj;
+ struct yaffs_obj *parent;
+ int i;
+ struct list_head *lh;
+ struct list_head *n;
+ int depth_limit;
+ int hanging;
+
+ if (dev->read_only)
+ return;
+
+ /* Iterate through the objects in each hash entry,
+ * looking at each object.
+ * Make sure it is rooted.
+ */
+
+ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) {
+ list_for_each_safe(lh, n, &dev->obj_bucket[i].list) {
+ obj = list_entry(lh, struct yaffs_obj, hash_link);
+ parent = obj->parent;
+
+ if (yaffs_has_null_parent(dev, obj)) {
+ /* These directories are not hanging */
+ hanging = 0;
+ } else if (!parent ||
+ parent->variant_type !=
+ YAFFS_OBJECT_TYPE_DIRECTORY) {
+ hanging = 1;
+ } else if (yaffs_has_null_parent(dev, parent)) {
+ hanging = 0;
+ } else {
+ /*
+ * Need to follow the parent chain to
+ * see if it is hanging.
+ */
+ hanging = 0;
+ depth_limit = 100;
+
+ while (parent != dev->root_dir &&
+ parent->parent &&
+ parent->parent->variant_type ==
+ YAFFS_OBJECT_TYPE_DIRECTORY &&
+ depth_limit > 0) {
+ parent = parent->parent;
+ depth_limit--;
+ }
+ if (parent != dev->root_dir)
+ hanging = 1;
+ }
+ if (hanging) {
+ yaffs_trace(YAFFS_TRACE_SCAN,
+ "Hanging object %d moved to lost and found",
+ obj->obj_id);
+ yaffs_add_obj_to_dir(dev->lost_n_found, obj);
+ }
+ }
+ }
+}
+
+/*
+ * Delete directory contents for cleaning up lost and found.
+ */
+static void yaffs_del_dir_contents(struct yaffs_obj *dir)
+{
+ struct yaffs_obj *obj;
+ struct list_head *lh;
+ struct list_head *n;
+
+ if (dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY)
+ BUG();
+
+ list_for_each_safe(lh, n, &dir->variant.dir_variant.children) {
+ obj = list_entry(lh, struct yaffs_obj, siblings);
+ if (obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY)
+ yaffs_del_dir_contents(obj);
+ yaffs_trace(YAFFS_TRACE_SCAN,
+ "Deleting lost_found object %d",
+ obj->obj_id);
+ yaffs_unlink_obj(obj);
+ }
+}
+
+static void yaffs_empty_l_n_f(struct yaffs_dev *dev)
+{
+ yaffs_del_dir_contents(dev->lost_n_found);
+}
+
+
+struct yaffs_obj *yaffs_find_by_name(struct yaffs_obj *directory,
+ const YCHAR *name)
+{
+ int sum;
+ struct list_head *i;
+ YCHAR buffer[YAFFS_MAX_NAME_LENGTH + 1];
+ struct yaffs_obj *l;
+
+ if (!name)
+ return NULL;
+
+ if (!directory) {
+ yaffs_trace(YAFFS_TRACE_ALWAYS,
+ "tragedy: yaffs_find_by_name: null pointer directory"
+ );
+ BUG();
+ return NULL;
+ }
+ if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {
+ yaffs_trace(YAFFS_TRACE_ALWAYS,
+ "tragedy: yaffs_find_by_name: non-directory"
+ );
+ BUG();
+ }
+
+ sum = yaffs_calc_name_sum(name);
+
+ list_for_each(i, &directory->variant.dir_variant.children) {
+ l = list_entry(i, struct yaffs_obj, siblings);
+
+ if (l->parent != directory)
+ BUG();
+
+ yaffs_check_obj_details_loaded(l);
+
+ /* Special case for lost-n-found */
+ if (l->obj_id == YAFFS_OBJECTID_LOSTNFOUND) {
+ if (!yaffs_strcmp(name, YAFFS_LOSTNFOUND_NAME))
+ return l;
+ } else if (l->sum == sum || l->hdr_chunk <= 0) {
+ /* LostnFound chunk called Objxxx
+ * Do a real check
+ */
+ yaffs_get_obj_name(l, buffer,
+ YAFFS_MAX_NAME_LENGTH + 1);
+ if (!yaffs_strncmp(name, buffer, YAFFS_MAX_NAME_LENGTH))
+ return l;
+ }
+ }
+ return NULL;
+}
+
+/* GetEquivalentObject dereferences any hard links to get to the
+ * actual object.
+ */
+
+struct yaffs_obj *yaffs_get_equivalent_obj(struct yaffs_obj *obj)
+{
+ if (obj && obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) {
+ obj = obj->variant.hardlink_variant.equiv_obj;
+ yaffs_check_obj_details_loaded(obj);
+ }
+ return obj;
+}
+
+/*
+ * A note or two on object names.
+ * * If the object name is missing, we then make one up in the form objnnn
+ *
+ * * ASCII names are stored in the object header's name field from byte zero
+ * * Unicode names are historically stored starting from byte zero.
+ *
+ * Then there are automatic Unicode names...
+ * The purpose of these is to save names in a way that can be read as
+ * ASCII or Unicode names as appropriate, thus allowing a Unicode and ASCII
+ * system to share files.
+ *
+ * These automatic unicode are stored slightly differently...
+ * - If the name can fit in the ASCII character space then they are saved as
+ * ascii names as per above.
+ * - If the name needs Unicode then the name is saved in Unicode
+ * starting at oh->name[1].
+
+ */
+static void yaffs_fix_null_name(struct yaffs_obj *obj, YCHAR *name,
+ int buffer_size)
+{
+ /* Create an object name if we could not find one. */
+ if (yaffs_strnlen(name, YAFFS_MAX_NAME_LENGTH) == 0) {
+ YCHAR local_name[20];
+ YCHAR num_string[20];
+ YCHAR *x = &num_string[19];
+ unsigned v = obj->obj_id;
+ num_string[19] = 0;
+ while (v > 0) {
+ x--;
+ *x = '0' + (v % 10);
+ v /= 10;
+ }
+ /* make up a name */
+ yaffs_strcpy(local_name, YAFFS_LOSTNFOUND_PREFIX);
+ yaffs_strcat(local_name, x);
+ yaffs_strncpy(name, local_name, buffer_size - 1);
+ }
+}
+
+int yaffs_get_obj_name(struct yaffs_obj *obj, YCHAR *name, int buffer_size)
+{
+ memset(name, 0, buffer_size * sizeof(YCHAR));
+ yaffs_check_obj_details_loaded(obj);
+ if (obj->obj_id == YAFFS_OBJECTID_LOSTNFOUND) {
+ yaffs_strncpy(name, YAFFS_LOSTNFOUND_NAME, buffer_size - 1);
+ } else if (obj->short_name[0]) {
+ yaffs_strcpy(name, obj->short_name);
+ } else if (obj->hdr_chunk > 0) {
+ u8 *buffer = yaffs_get_temp_buffer(obj->my_dev);
+
+ struct yaffs_obj_hdr *oh = (struct yaffs_obj_hdr *)buffer;
+
+ memset(buffer, 0, obj->my_dev->data_bytes_per_chunk);
+
+ if (obj->hdr_chunk > 0) {
+ yaffs_rd_chunk_tags_nand(obj->my_dev,
+ obj->hdr_chunk,
+ buffer, NULL);
+ }
+ yaffs_load_name_from_oh(obj->my_dev, name, oh->name,
+ buffer_size);
+
+ yaffs_release_temp_buffer(obj->my_dev, buffer);
+ }
+
+ yaffs_fix_null_name(obj, name, buffer_size);
+
+ return yaffs_strnlen(name, YAFFS_MAX_NAME_LENGTH);
+}
+
+loff_t yaffs_get_obj_length(struct yaffs_obj *obj)
+{
+ /* Dereference any hard linking */
+ obj = yaffs_get_equivalent_obj(obj);
+
+ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE)
+ return obj->variant.file_variant.file_size;
+ if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) {
+ if (!obj->variant.symlink_variant.alias)
+ return 0;
+ return yaffs_strnlen(obj->variant.symlink_variant.alias,
+ YAFFS_MAX_ALIAS_LENGTH);
+ } else {
+ /* Only a directory should drop through to here */
+ return obj->my_dev->data_bytes_per_chunk;
+ }
+}
+
+int yaffs_get_obj_link_count(struct yaffs_obj *obj)
+{
+ int count = 0;
+ struct list_head *i;
+
+ if (!obj->unlinked)
+ count++; /* the object itself */
+
+ list_for_each(i, &obj->hard_links)
+ count++; /* add the hard links; */
+
+ return count;
+}
+
+int yaffs_get_obj_inode(struct yaffs_obj *obj)
+{
+ obj = yaffs_get_equivalent_obj(obj);
+
+ return obj->obj_id;
+}
+
+unsigned yaffs_get_obj_type(struct yaffs_obj *obj)
+{
+ obj = yaffs_get_equivalent_obj(obj);
+
+ switch (obj->variant_type) {
+ case YAFFS_OBJECT_TYPE_FILE:
+ return DT_REG;
+ break;
+ case YAFFS_OBJECT_TYPE_DIRECTORY:
+ return DT_DIR;
+ break;
+ case YAFFS_OBJECT_TYPE_SYMLINK:
+ return DT_LNK;
+ break;
+ case YAFFS_OBJECT_TYPE_HARDLINK:
+ return DT_REG;
+ break;
+ case YAFFS_OBJECT_TYPE_SPECIAL:
+ if (S_ISFIFO(obj->yst_mode))
+ return DT_FIFO;
+ if (S_ISCHR(obj->yst_mode))
+ return DT_CHR;
+ if (S_ISBLK(obj->yst_mode))
+ return DT_BLK;
+ if (S_ISSOCK(obj->yst_mode))
+ return DT_SOCK;
+ return DT_REG;
+ break;
+ default:
+ return DT_REG;
+ break;
+ }
+}
+
+YCHAR *yaffs_get_symlink_alias(struct yaffs_obj *obj)
+{
+ obj = yaffs_get_equivalent_obj(obj);
+ if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK)
+ return yaffs_clone_str(obj->variant.symlink_variant.alias);
+ else
+ return yaffs_clone_str(_Y(""));
+}
+
+/*--------------------------- Initialisation code -------------------------- */
+
+static int yaffs_check_dev_fns(const struct yaffs_dev *dev)
+{
+ /* Common functions, gotta have */
+ if (!dev->param.erase_fn || !dev->param.initialise_flash_fn)
+ return 0;
+
+ /* Can use the "with tags" style interface for yaffs1 or yaffs2 */
+ if (dev->param.write_chunk_tags_fn &&
+ dev->param.read_chunk_tags_fn &&
+ !dev->param.write_chunk_fn &&
+ !dev->param.read_chunk_fn &&
+ dev->param.bad_block_fn && dev->param.query_block_fn)
+ return 1;
+
+ /* Can use the "spare" style interface for yaffs1 */
+ if (!dev->param.is_yaffs2 &&
+ !dev->param.write_chunk_tags_fn &&
+ !dev->param.read_chunk_tags_fn &&
+ dev->param.write_chunk_fn &&
+ dev->param.read_chunk_fn &&
+ !dev->param.bad_block_fn && !dev->param.query_block_fn)
+ return 1;
+
+ return 0; /* bad */
+}
+
+static int yaffs_create_initial_dir(struct yaffs_dev *dev)
+{
+ /* Initialise the unlinked, deleted, root and lost+found directories */
+ dev->lost_n_found = dev->root_dir = NULL;
+ dev->unlinked_dir = dev->del_dir = NULL;
+ dev->unlinked_dir =
+ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_UNLINKED, S_IFDIR);
+ dev->del_dir =
+ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_DELETED, S_IFDIR);
+ dev->root_dir =
+ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_ROOT,
+ YAFFS_ROOT_MODE | S_IFDIR);
+ dev->lost_n_found =
+ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_LOSTNFOUND,
+ YAFFS_LOSTNFOUND_MODE | S_IFDIR);
+
+ if (dev->lost_n_found && dev->root_dir && dev->unlinked_dir
+ && dev->del_dir) {
+ yaffs_add_obj_to_dir(dev->root_dir, dev->lost_n_found);
+ return YAFFS_OK;
+ }
+ return YAFFS_FAIL;
+}
+
+int yaffs_guts_initialise(struct yaffs_dev *dev)
+{
+ int init_failed = 0;
+ unsigned x;
+ int bits;
+
+ yaffs_trace(YAFFS_TRACE_TRACING, "yaffs: yaffs_guts_initialise()");
+
+ /* Check stuff that must be set */
+
+ if (!dev) {
+ yaffs_trace(YAFFS_TRACE_ALWAYS,
+ "yaffs: Need a device"
+ );
+ return YAFFS_FAIL;
+ }
+
+ if (dev->is_mounted) {
+ yaffs_trace(YAFFS_TRACE_ALWAYS, "device already mounted");
+ return YAFFS_FAIL;
+ }
+
+ dev->internal_start_block = dev->param.start_block;
+ dev->internal_end_block = dev->param.end_block;
+ dev->block_offset = 0;
+ dev->chunk_offset = 0;
+ dev->n_free_chunks = 0;
+
+ dev->gc_block = 0;
+
+ if (dev->param.start_block == 0) {
+ dev->internal_start_block = dev->param.start_block + 1;
+ dev->internal_end_block = dev->param.end_block + 1;
+ dev->block_offset = 1;
+ dev->chunk_offset = dev->param.chunks_per_block;
+ }
+
+ /* Check geometry parameters. */
+
+ if ((!dev->param.inband_tags && dev->param.is_yaffs2 &&
+ dev->param.total_bytes_per_chunk < 1024) ||
+ (!dev->param.is_yaffs2 &&
+ dev->param.total_bytes_per_chunk < 512) ||
+ (dev->param.inband_tags && !dev->param.is_yaffs2) ||
+ dev->param.chunks_per_block < 2 ||
+ dev->param.n_reserved_blocks < 2 ||
+ dev->internal_start_block <= 0 ||
+ dev->internal_end_block <= 0 ||
+ dev->internal_end_block <=
+ (dev->internal_start_block + dev->param.n_reserved_blocks + 2)
+ ) {
+ /* otherwise it is too small */
+ yaffs_trace(YAFFS_TRACE_ALWAYS,
+ "NAND geometry problems: chunk size %d, type is yaffs%s, inband_tags %d ",
+ dev->param.total_bytes_per_chunk,
+ dev->param.is_yaffs2 ? "2" : "",
+ dev->param.inband_tags);
+ return YAFFS_FAIL;
+ }
+
+ if (yaffs_init_nand(dev) != YAFFS_OK) {
+ yaffs_trace(YAFFS_TRACE_ALWAYS, "InitialiseNAND failed");
+ return YAFFS_FAIL;
+ }
+
+ /* Sort out space for inband tags, if required */
+ if (dev->param.inband_tags)
+ dev->data_bytes_per_chunk =
+ dev->param.total_bytes_per_chunk -
+ sizeof(struct yaffs_packed_tags2_tags_only);
+ else
+ dev->data_bytes_per_chunk = dev->param.total_bytes_per_chunk;
+
+ /* Got the right mix of functions? */
+ if (!yaffs_check_dev_fns(dev)) {
+ /* Function missing */
+ yaffs_trace(YAFFS_TRACE_ALWAYS,
+ "device function(s) missing or wrong");
+
+ return YAFFS_FAIL;
+ }
+
+ /* Finished with most checks. Further checks happen later on too. */
+
+ dev->is_mounted = 1;
+
+ /* OK now calculate a few things for the device */
+
+ /*
+ * Calculate all the chunk size manipulation numbers:
+ */
+ x = dev->data_bytes_per_chunk;
+ /* We always use dev->chunk_shift and dev->chunk_div */
+ dev->chunk_shift = calc_shifts(x);
+ x >>= dev->chunk_shift;
+ dev->chunk_div = x;
+ /* We only use chunk mask if chunk_div is 1 */
+ dev->chunk_mask = (1 << dev->chunk_shift) - 1;
+
+ /*
+ * Calculate chunk_grp_bits.
+ * We need to find the next power of 2 > than internal_end_block
+ */
+
+ x = dev->param.chunks_per_block * (dev->internal_end_block + 1);
+
+ bits = calc_shifts_ceiling(x);
+
+ /* Set up tnode width if wide tnodes are enabled. */
+ if (!dev->param.wide_tnodes_disabled) {
+ /* bits must be even so that we end up with 32-bit words */
+ if (bits & 1)
+ bits++;
+ if (bits < 16)
+ dev->tnode_width = 16;
+ else
+ dev->tnode_width = bits;
+ } else {
+ dev->tnode_width = 16;
+ }
+
+ dev->tnode_mask = (1 << dev->tnode_width) - 1;
+
+ /* Level0 Tnodes are 16 bits or wider (if wide tnodes are enabled),
+ * so if the bitwidth of the
+ * chunk range we're using is greater than 16 we need
+ * to figure out chunk shift and chunk_grp_size
+ */
+
+ if (bits <= dev->tnode_width)
+ dev->chunk_grp_bits = 0;
+ else
+ dev->chunk_grp_bits = bits - dev->tnode_width;
+
+ dev->tnode_size = (dev->tnode_width * YAFFS_NTNODES_LEVEL0) / 8;
+ if (dev->tnode_size < sizeof(struct yaffs_tnode))
+ dev->tnode_size = sizeof(struct yaffs_tnode);
+
+ dev->chunk_grp_size = 1 << dev->chunk_grp_bits;
+
+ if (dev->param.chunks_per_block < dev->chunk_grp_size) {
+ /* We have a problem because the soft delete won't work if
+ * the chunk group size > chunks per block.
+ * This can be remedied by using larger "virtual blocks".
+ */
+ yaffs_trace(YAFFS_TRACE_ALWAYS, "chunk group too large");
+
+ return YAFFS_FAIL;
+ }
+
+ /* Finished verifying the device, continue with initialisation */
+
+ /* More device initialisation */
+ dev->all_gcs = 0;
+ dev->passive_gc_count = 0;
+ dev->oldest_dirty_gc_count = 0;
+ dev->bg_gcs = 0;
+ dev->gc_block_finder = 0;
+ dev->buffered_block = -1;
+ dev->doing_buffered_block_rewrite = 0;
+ dev->n_deleted_files = 0;
+ dev->n_bg_deletions = 0;
+ dev->n_unlinked_files = 0;
+ dev->n_ecc_fixed = 0;
+ dev->n_ecc_unfixed = 0;
+ dev->n_tags_ecc_fixed = 0;
+ dev->n_tags_ecc_unfixed = 0;
+ dev->n_erase_failures = 0;
+ dev->n_erased_blocks = 0;
+ dev->gc_disable = 0;
+ dev->has_pending_prioritised_gc = 1;
+ /* Assume the worst for now, will get fixed on first GC */
+ INIT_LIST_HEAD(&dev->dirty_dirs);
+ dev->oldest_dirty_seq = 0;
+ dev->oldest_dirty_block = 0;
+
+ /* Initialise temporary buffers and caches. */
+ if (!yaffs_init_tmp_buffers(dev))
+ init_failed = 1;
+
+ dev->cache = NULL;
+ dev->gc_cleanup_list = NULL;
+
+ if (!init_failed && dev->param.n_caches > 0) {
+ int i;
+ void *buf;
+ int cache_bytes =
+ dev->param.n_caches * sizeof(struct yaffs_cache);
+
+ if (dev->param.n_caches > YAFFS_MAX_SHORT_OP_CACHES)
+ dev->param.n_caches = YAFFS_MAX_SHORT_OP_CACHES;
+
+ dev->cache = kmalloc(cache_bytes, GFP_NOFS);
+
+ buf = (u8 *) dev->cache;
+
+ if (dev->cache)
+ memset(dev->cache, 0, cache_bytes);
+
+ for (i = 0; i < dev->param.n_caches && buf; i++) {
+ dev->cache[i].object = NULL;
+ dev->cache[i].last_use = 0;
+ dev->cache[i].dirty = 0;
+ dev->cache[i].data = buf =
+ kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS);
+ }
+ if (!buf)
+ init_failed = 1;
+
+ dev->cache_last_use = 0;
+ }
+
+ dev->cache_hits = 0;
+
+ if (!init_failed) {
+ dev->gc_cleanup_list =
+ kmalloc(dev->param.chunks_per_block * sizeof(u32),
+ GFP_NOFS);
+ if (!dev->gc_cleanup_list)
+ init_failed = 1;
+ }
+
+ if (dev->param.is_yaffs2)
+ dev->param.use_header_file_size = 1;
+
+ if (!init_failed && !yaffs_init_blocks(dev))
+ init_failed = 1;
+
+ yaffs_init_tnodes_and_objs(dev);
+
+ if (!init_failed && !yaffs_create_initial_dir(dev))
+ init_failed = 1;
+
+ if (!init_failed && dev->param.is_yaffs2 &&
+ !dev->param.disable_summary &&
+ !yaffs_summary_init(dev))
+ init_failed = 1;
+
+ if (!init_failed) {
+ /* Now scan the flash. */
+ if (dev->param.is_yaffs2) {
+ if (yaffs2_checkpt_restore(dev)) {
+ yaffs_check_obj_details_loaded(dev->root_dir);
+ yaffs_trace(YAFFS_TRACE_CHECKPOINT |
+ YAFFS_TRACE_MOUNT,
+ "yaffs: restored from checkpoint"
+ );
+ } else {
+
+ /* Clean up the mess caused by an aborted
+ * checkpoint load then scan backwards.
+ */
+ yaffs_deinit_blocks(dev);
+
+ yaffs_deinit_tnodes_and_objs(dev);
+
+ dev->n_erased_blocks = 0;
+ dev->n_free_chunks = 0;
+ dev->alloc_block = -1;
+ dev->alloc_page = -1;
+ dev->n_deleted_files = 0;
+ dev->n_unlinked_files = 0;
+ dev->n_bg_deletions = 0;
+
+ if (!init_failed && !yaffs_init_blocks(dev))
+ init_failed = 1;
+
+ yaffs_init_tnodes_and_objs(dev);
+
+ if (!init_failed
+ && !yaffs_create_initial_dir(dev))
+ init_failed = 1;
+
+ if (!init_failed && !yaffs2_scan_backwards(dev))
+ init_failed = 1;
+ }
+ } else if (!yaffs1_scan(dev)) {
+ init_failed = 1;
+ }
+
+ yaffs_strip_deleted_objs(dev);
+ yaffs_fix_hanging_objs(dev);
+ if (dev->param.empty_lost_n_found)
+ yaffs_empty_l_n_f(dev);
+ }
+
+ if (init_failed) {
+ /* Clean up the mess */
+ yaffs_trace(YAFFS_TRACE_TRACING,
+ "yaffs: yaffs_guts_initialise() aborted.");
+
+ yaffs_deinitialise(dev);
+ return YAFFS_FAIL;
+ }
+
+ /* Zero out stats */
+ dev->n_page_reads = 0;
+ dev->n_page_writes = 0;
+ dev->n_erasures = 0;
+ dev->n_gc_copies = 0;
+ dev->n_retried_writes = 0;
+
+ dev->n_retired_blocks = 0;
+
+ yaffs_verify_free_chunks(dev);
+ yaffs_verify_blocks(dev);
+
+ /* Clean up any aborted checkpoint data */
+ if (!dev->is_checkpointed && dev->blocks_in_checkpt > 0)
+ yaffs2_checkpt_invalidate(dev);
+
+ yaffs_trace(YAFFS_TRACE_TRACING,
+ "yaffs: yaffs_guts_initialise() done.");
+ return YAFFS_OK;
+}
+
+void yaffs_deinitialise(struct yaffs_dev *dev)
+{
+ if (dev->is_mounted) {
+ int i;
+
+ yaffs_deinit_blocks(dev);
+ yaffs_deinit_tnodes_and_objs(dev);
+ yaffs_summary_deinit(dev);
+
+ if (dev->param.n_caches > 0 && dev->cache) {
+
+ for (i = 0; i < dev->param.n_caches; i++) {
+ kfree(dev->cache[i].data);
+ dev->cache[i].data = NULL;
+ }
+
+ kfree(dev->cache);
+ dev->cache = NULL;
+ }
+
+ kfree(dev->gc_cleanup_list);
+
+ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++)
+ kfree(dev->temp_buffer[i].buffer);
+
+ dev->is_mounted = 0;
+
+ if (dev->param.deinitialise_flash_fn)
+ dev->param.deinitialise_flash_fn(dev);
+ }
+}
+
+int yaffs_count_free_chunks(struct yaffs_dev *dev)
+{
+ int n_free = 0;
+ int b;
+ struct yaffs_block_info *blk;
+
+ blk = dev->block_info;
+ for (b = dev->internal_start_block; b <= dev->internal_end_block; b++) {
+ switch (blk->block_state) {
+ case YAFFS_BLOCK_STATE_EMPTY:
+ case YAFFS_BLOCK_STATE_ALLOCATING:
+ case YAFFS_BLOCK_STATE_COLLECTING:
+ case YAFFS_BLOCK_STATE_FULL:
+ n_free +=
+ (dev->param.chunks_per_block - blk->pages_in_use +
+ blk->soft_del_pages);
+ break;
+ default:
+ break;
+ }
+ blk++;
+ }
+ return n_free;
+}
+
+int yaffs_get_n_free_chunks(struct yaffs_dev *dev)
+{
+ /* This is what we report to the outside world */
+ int n_free;
+ int n_dirty_caches;
+ int blocks_for_checkpt;
+ int i;
+
+ n_free = dev->n_free_chunks;
+ n_free += dev->n_deleted_files;
+
+ /* Now count and subtract the number of dirty chunks in the cache. */
+
+ for (n_dirty_caches = 0, i = 0; i < dev->param.n_caches; i++) {
+ if (dev->cache[i].dirty)
+ n_dirty_caches++;
+ }
+
+ n_free -= n_dirty_caches;
+
+ n_free -=
+ ((dev->param.n_reserved_blocks + 1) * dev->param.chunks_per_block);
+
+ /* Now figure checkpoint space and report that... */
+ blocks_for_checkpt = yaffs_calc_checkpt_blocks_required(dev);
+
+ n_free -= (blocks_for_checkpt * dev->param.chunks_per_block);
+
+ if (n_free < 0)
+ n_free = 0;
+
+ return n_free;
+}
+
+/*\
+ * Marshalling functions to get loff_t file sizes into aand out of
+ * object headers.
+ */
+void yaffs_oh_size_load(struct yaffs_obj_hdr *oh, loff_t fsize)
+{
+ oh->file_size_low = (fsize & 0xFFFFFFFF);
+ oh->file_size_high = ((fsize >> 32) & 0xFFFFFFFF);
+}
+
+loff_t yaffs_oh_to_size(struct yaffs_obj_hdr *oh)
+{
+ loff_t retval;
+
+ if (~(oh->file_size_high))
+ retval = (((loff_t) oh->file_size_high) << 32) |
+ (((loff_t) oh->file_size_low) & 0xFFFFFFFF);
+ else
+ retval = (loff_t) oh->file_size_low;
+
+ return retval;
+}
diff --git a/roms/u-boot/fs/yaffs2/yaffs_guts.h b/roms/u-boot/fs/yaffs2/yaffs_guts.h
new file mode 100644
index 000000000..e3558c5a6
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_guts.h
@@ -0,0 +1,973 @@
+/*
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ */
+
+#ifndef __YAFFS_GUTS_H__
+#define __YAFFS_GUTS_H__
+
+#include "yportenv.h"
+
+#define YAFFS_OK 1
+#define YAFFS_FAIL 0
+
+/* Give us a Y=0x59,
+ * Give us an A=0x41,
+ * Give us an FF=0xff
+ * Give us an S=0x53
+ * And what have we got...
+ */
+#define YAFFS_MAGIC 0x5941ff53
+
+/*
+ * Tnodes form a tree with the tnodes in "levels"
+ * Levels greater than 0 hold 8 slots which point to other tnodes.
+ * Those at level 0 hold 16 slots which point to chunks in NAND.
+ *
+ * A maximum level of 8 thust supports files of size up to:
+ *
+ * 2^(3*MAX_LEVEL+4)
+ *
+ * Thus a max level of 8 supports files with up to 2^^28 chunks which gives
+ * a maximum file size of arounf 51Gbytees with 2k chunks.
+ */
+#define YAFFS_NTNODES_LEVEL0 16
+#define YAFFS_TNODES_LEVEL0_BITS 4
+#define YAFFS_TNODES_LEVEL0_MASK 0xf
+
+#define YAFFS_NTNODES_INTERNAL (YAFFS_NTNODES_LEVEL0 / 2)
+#define YAFFS_TNODES_INTERNAL_BITS (YAFFS_TNODES_LEVEL0_BITS - 1)
+#define YAFFS_TNODES_INTERNAL_MASK 0x7
+#define YAFFS_TNODES_MAX_LEVEL 8
+#define YAFFS_TNODES_MAX_BITS (YAFFS_TNODES_LEVEL0_BITS + \
+ YAFFS_TNODES_INTERNAL_BITS * \
+ YAFFS_TNODES_MAX_LEVEL)
+#define YAFFS_MAX_CHUNK_ID ((1 << YAFFS_TNODES_MAX_BITS) - 1)
+
+/* Constants for YAFFS1 mode */
+#define YAFFS_BYTES_PER_SPARE 16
+#define YAFFS_BYTES_PER_CHUNK 512
+#define YAFFS_CHUNK_SIZE_SHIFT 9
+#define YAFFS_CHUNKS_PER_BLOCK 32
+#define YAFFS_BYTES_PER_BLOCK (YAFFS_CHUNKS_PER_BLOCK*YAFFS_BYTES_PER_CHUNK)
+
+#define YAFFS_MIN_YAFFS2_CHUNK_SIZE 1024
+#define YAFFS_MIN_YAFFS2_SPARE_SIZE 32
+
+
+
+#define YAFFS_ALLOCATION_NOBJECTS 100
+#define YAFFS_ALLOCATION_NTNODES 100
+#define YAFFS_ALLOCATION_NLINKS 100
+
+#define YAFFS_NOBJECT_BUCKETS 256
+
+#define YAFFS_OBJECT_SPACE 0x40000
+#define YAFFS_MAX_OBJECT_ID (YAFFS_OBJECT_SPACE - 1)
+
+/* Binary data version stamps */
+#define YAFFS_SUMMARY_VERSION 1
+#define YAFFS_CHECKPOINT_VERSION 6
+
+#ifdef CONFIG_YAFFS_UNICODE
+#define YAFFS_MAX_NAME_LENGTH 127
+#define YAFFS_MAX_ALIAS_LENGTH 79
+#else
+#define YAFFS_MAX_NAME_LENGTH 255
+#define YAFFS_MAX_ALIAS_LENGTH 159
+#endif
+
+#define YAFFS_SHORT_NAME_LENGTH 15
+
+/* Some special object ids for pseudo objects */
+#define YAFFS_OBJECTID_ROOT 1
+#define YAFFS_OBJECTID_LOSTNFOUND 2
+#define YAFFS_OBJECTID_UNLINKED 3
+#define YAFFS_OBJECTID_DELETED 4
+
+/* Fake object Id for summary data */
+#define YAFFS_OBJECTID_SUMMARY 0x10
+
+/* Pseudo object ids for checkpointing */
+#define YAFFS_OBJECTID_CHECKPOINT_DATA 0x20
+#define YAFFS_SEQUENCE_CHECKPOINT_DATA 0x21
+
+#define YAFFS_MAX_SHORT_OP_CACHES 20
+
+#define YAFFS_N_TEMP_BUFFERS 6
+
+/* We limit the number attempts at sucessfully saving a chunk of data.
+ * Small-page devices have 32 pages per block; large-page devices have 64.
+ * Default to something in the order of 5 to 10 blocks worth of chunks.
+ */
+#define YAFFS_WR_ATTEMPTS (5*64)
+
+/* Sequence numbers are used in YAFFS2 to determine block allocation order.
+ * The range is limited slightly to help distinguish bad numbers from good.
+ * This also allows us to perhaps in the future use special numbers for
+ * special purposes.
+ * EFFFFF00 allows the allocation of 8 blocks/second (~1Mbytes) for 15 years,
+ * and is a larger number than the lifetime of a 2GB device.
+ */
+#define YAFFS_LOWEST_SEQUENCE_NUMBER 0x00001000
+#define YAFFS_HIGHEST_SEQUENCE_NUMBER 0xefffff00
+
+/* Special sequence number for bad block that failed to be marked bad */
+#define YAFFS_SEQUENCE_BAD_BLOCK 0xffff0000
+
+/* ChunkCache is used for short read/write operations.*/
+struct yaffs_cache {
+ struct yaffs_obj *object;
+ int chunk_id;
+ int last_use;
+ int dirty;
+ int n_bytes; /* Only valid if the cache is dirty */
+ int locked; /* Can't push out or flush while locked. */
+ u8 *data;
+};
+
+/* yaffs1 tags structures in RAM
+ * NB This uses bitfield. Bitfields should not straddle a u32 boundary
+ * otherwise the structure size will get blown out.
+ */
+
+struct yaffs_tags {
+ unsigned chunk_id:20;
+ unsigned serial_number:2;
+ unsigned n_bytes_lsb:10;
+ unsigned obj_id:18;
+ unsigned ecc:12;
+ unsigned n_bytes_msb:2;
+};
+
+union yaffs_tags_union {
+ struct yaffs_tags as_tags;
+ u8 as_bytes[8];
+};
+
+
+/* Stuff used for extended tags in YAFFS2 */
+
+enum yaffs_ecc_result {
+ YAFFS_ECC_RESULT_UNKNOWN,
+ YAFFS_ECC_RESULT_NO_ERROR,
+ YAFFS_ECC_RESULT_FIXED,
+ YAFFS_ECC_RESULT_UNFIXED
+};
+
+enum yaffs_obj_type {
+ YAFFS_OBJECT_TYPE_UNKNOWN,
+ YAFFS_OBJECT_TYPE_FILE,
+ YAFFS_OBJECT_TYPE_SYMLINK,
+ YAFFS_OBJECT_TYPE_DIRECTORY,
+ YAFFS_OBJECT_TYPE_HARDLINK,
+ YAFFS_OBJECT_TYPE_SPECIAL
+};
+
+#define YAFFS_OBJECT_TYPE_MAX YAFFS_OBJECT_TYPE_SPECIAL
+
+struct yaffs_ext_tags {
+ unsigned chunk_used; /* Status of the chunk: used or unused */
+ unsigned obj_id; /* If 0 this is not used */
+ unsigned chunk_id; /* If 0 this is a header, else a data chunk */
+ unsigned n_bytes; /* Only valid for data chunks */
+
+ /* The following stuff only has meaning when we read */
+ enum yaffs_ecc_result ecc_result;
+ unsigned block_bad;
+
+ /* YAFFS 1 stuff */
+ unsigned is_deleted; /* The chunk is marked deleted */
+ unsigned serial_number; /* Yaffs1 2-bit serial number */
+
+ /* YAFFS2 stuff */
+ unsigned seq_number; /* The sequence number of this block */
+
+ /* Extra info if this is an object header (YAFFS2 only) */
+
+ unsigned extra_available; /* Extra info available if not zero */
+ unsigned extra_parent_id; /* The parent object */
+ unsigned extra_is_shrink; /* Is it a shrink header? */
+ unsigned extra_shadows; /* Does this shadow another object? */
+
+ enum yaffs_obj_type extra_obj_type; /* What object type? */
+
+ loff_t extra_file_size; /* Length if it is a file */
+ unsigned extra_equiv_id; /* Equivalent object for a hard link */
+};
+
+/* Spare structure for YAFFS1 */
+struct yaffs_spare {
+ u8 tb0;
+ u8 tb1;
+ u8 tb2;
+ u8 tb3;
+ u8 page_status; /* set to 0 to delete the chunk */
+ u8 block_status;
+ u8 tb4;
+ u8 tb5;
+ u8 ecc1[3];
+ u8 tb6;
+ u8 tb7;
+ u8 ecc2[3];
+};
+
+/*Special structure for passing through to mtd */
+struct yaffs_nand_spare {
+ struct yaffs_spare spare;
+ int eccres1;
+ int eccres2;
+};
+
+/* Block data in RAM */
+
+enum yaffs_block_state {
+ YAFFS_BLOCK_STATE_UNKNOWN = 0,
+
+ YAFFS_BLOCK_STATE_SCANNING,
+ /* Being scanned */
+
+ YAFFS_BLOCK_STATE_NEEDS_SCAN,
+ /* The block might have something on it (ie it is allocating or full,
+ * perhaps empty) but it needs to be scanned to determine its true
+ * state.
+ * This state is only valid during scanning.
+ * NB We tolerate empty because the pre-scanner might be incapable of
+ * deciding
+ * However, if this state is returned on a YAFFS2 device,
+ * then we expect a sequence number
+ */
+
+ YAFFS_BLOCK_STATE_EMPTY,
+ /* This block is empty */
+
+ YAFFS_BLOCK_STATE_ALLOCATING,
+ /* This block is partially allocated.
+ * At least one page holds valid data.
+ * This is the one currently being used for page
+ * allocation. Should never be more than one of these.
+ * If a block is only partially allocated at mount it is treated as
+ * full.
+ */
+
+ YAFFS_BLOCK_STATE_FULL,
+ /* All the pages in this block have been allocated.
+ * If a block was only partially allocated when mounted we treat
+ * it as fully allocated.
+ */
+
+ YAFFS_BLOCK_STATE_DIRTY,
+ /* The block was full and now all chunks have been deleted.
+ * Erase me, reuse me.
+ */
+
+ YAFFS_BLOCK_STATE_CHECKPOINT,
+ /* This block is assigned to holding checkpoint data. */
+
+ YAFFS_BLOCK_STATE_COLLECTING,
+ /* This block is being garbage collected */
+
+ YAFFS_BLOCK_STATE_DEAD
+ /* This block has failed and is not in use */
+};
+
+#define YAFFS_NUMBER_OF_BLOCK_STATES (YAFFS_BLOCK_STATE_DEAD + 1)
+
+struct yaffs_block_info {
+
+ int soft_del_pages:10; /* number of soft deleted pages */
+ int pages_in_use:10; /* number of pages in use */
+ unsigned block_state:4; /* One of the above block states. */
+ /* NB use unsigned because enum is sometimes
+ * an int */
+ u32 needs_retiring:1; /* Data has failed on this block, */
+ /*need to get valid data off and retire*/
+ u32 skip_erased_check:1;/* Skip the erased check on this block */
+ u32 gc_prioritise:1; /* An ECC check or blank check has failed.
+ Block should be prioritised for GC */
+ u32 chunk_error_strikes:3; /* How many times we've had ecc etc
+ failures on this block and tried to reuse it */
+ u32 has_summary:1; /* The block has a summary */
+
+ u32 has_shrink_hdr:1; /* This block has at least one shrink header */
+ u32 seq_number; /* block sequence number for yaffs2 */
+
+};
+
+/* -------------------------- Object structure -------------------------------*/
+/* This is the object structure as stored on NAND */
+
+struct yaffs_obj_hdr {
+ enum yaffs_obj_type type;
+
+ /* Apply to everything */
+ int parent_obj_id;
+ u16 sum_no_longer_used; /* checksum of name. No longer used */
+ YCHAR name[YAFFS_MAX_NAME_LENGTH + 1];
+
+ /* The following apply to all object types except for hard links */
+ u32 yst_mode; /* protection */
+
+ u32 yst_uid;
+ u32 yst_gid;
+ u32 yst_atime;
+ u32 yst_mtime;
+ u32 yst_ctime;
+
+ /* File size applies to files only */
+ u32 file_size_low;
+
+ /* Equivalent object id applies to hard links only. */
+ int equiv_id;
+
+ /* Alias is for symlinks only. */
+ YCHAR alias[YAFFS_MAX_ALIAS_LENGTH + 1];
+
+ u32 yst_rdev; /* stuff for block and char devices (major/min) */
+
+ u32 win_ctime[2];
+ u32 win_atime[2];
+ u32 win_mtime[2];
+
+ u32 inband_shadowed_obj_id;
+ u32 inband_is_shrink;
+
+ u32 file_size_high;
+ u32 reserved[1];
+ int shadows_obj; /* This object header shadows the
+ specified object if > 0 */
+
+ /* is_shrink applies to object headers written when wemake a hole. */
+ u32 is_shrink;
+
+};
+
+/*--------------------------- Tnode -------------------------- */
+
+struct yaffs_tnode {
+ struct yaffs_tnode *internal[YAFFS_NTNODES_INTERNAL];
+};
+
+/*------------------------ Object -----------------------------*/
+/* An object can be one of:
+ * - a directory (no data, has children links
+ * - a regular file (data.... not prunes :->).
+ * - a symlink [symbolic link] (the alias).
+ * - a hard link
+ */
+
+struct yaffs_file_var {
+ loff_t file_size;
+ loff_t scanned_size;
+ loff_t shrink_size;
+ int top_level;
+ struct yaffs_tnode *top;
+};
+
+struct yaffs_dir_var {
+ struct list_head children; /* list of child links */
+ struct list_head dirty; /* Entry for list of dirty directories */
+};
+
+struct yaffs_symlink_var {
+ YCHAR *alias;
+};
+
+struct yaffs_hardlink_var {
+ struct yaffs_obj *equiv_obj;
+ u32 equiv_id;
+};
+
+union yaffs_obj_var {
+ struct yaffs_file_var file_variant;
+ struct yaffs_dir_var dir_variant;
+ struct yaffs_symlink_var symlink_variant;
+ struct yaffs_hardlink_var hardlink_variant;
+};
+
+struct yaffs_obj {
+ u8 deleted:1; /* This should only apply to unlinked files. */
+ u8 soft_del:1; /* it has also been soft deleted */
+ u8 unlinked:1; /* An unlinked file.*/
+ u8 fake:1; /* A fake object has no presence on NAND. */
+ u8 rename_allowed:1; /* Some objects cannot be renamed. */
+ u8 unlink_allowed:1;
+ u8 dirty:1; /* the object needs to be written to flash */
+ u8 valid:1; /* When the file system is being loaded up, this
+ * object might be created before the data
+ * is available
+ * ie. file data chunks encountered before
+ * the header.
+ */
+ u8 lazy_loaded:1; /* This object has been lazy loaded and
+ * is missing some detail */
+
+ u8 defered_free:1; /* Object is removed from NAND, but is
+ * still in the inode cache.
+ * Free of object is defered.
+ * until the inode is released.
+ */
+ u8 being_created:1; /* This object is still being created
+ * so skip some verification checks. */
+ u8 is_shadowed:1; /* This object is shadowed on the way
+ * to being renamed. */
+
+ u8 xattr_known:1; /* We know if this has object has xattribs
+ * or not. */
+ u8 has_xattr:1; /* This object has xattribs.
+ * Only valid if xattr_known. */
+
+ u8 serial; /* serial number of chunk in NAND.*/
+ u16 sum; /* sum of the name to speed searching */
+
+ struct yaffs_dev *my_dev; /* The device I'm on */
+
+ struct list_head hash_link; /* list of objects in hash bucket */
+
+ struct list_head hard_links; /* hard linked object chain*/
+
+ /* directory structure stuff */
+ /* also used for linking up the free list */
+ struct yaffs_obj *parent;
+ struct list_head siblings;
+
+ /* Where's my object header in NAND? */
+ int hdr_chunk;
+
+ int n_data_chunks; /* Number of data chunks for this file. */
+
+ u32 obj_id; /* the object id value */
+
+ u32 yst_mode;
+
+ YCHAR short_name[YAFFS_SHORT_NAME_LENGTH + 1];
+
+#ifdef CONFIG_YAFFS_WINCE
+ u32 win_ctime[2];
+ u32 win_mtime[2];
+ u32 win_atime[2];
+#else
+ u32 yst_uid;
+ u32 yst_gid;
+ u32 yst_atime;
+ u32 yst_mtime;
+ u32 yst_ctime;
+#endif
+
+ u32 yst_rdev;
+
+ void *my_inode;
+
+ enum yaffs_obj_type variant_type;
+
+ union yaffs_obj_var variant;
+
+};
+
+struct yaffs_obj_bucket {
+ struct list_head list;
+ int count;
+};
+
+/* yaffs_checkpt_obj holds the definition of an object as dumped
+ * by checkpointing.
+ */
+
+struct yaffs_checkpt_obj {
+ int struct_type;
+ u32 obj_id;
+ u32 parent_id;
+ int hdr_chunk;
+ enum yaffs_obj_type variant_type:3;
+ u8 deleted:1;
+ u8 soft_del:1;
+ u8 unlinked:1;
+ u8 fake:1;
+ u8 rename_allowed:1;
+ u8 unlink_allowed:1;
+ u8 serial;
+ int n_data_chunks;
+ loff_t size_or_equiv_obj;
+};
+
+/*--------------------- Temporary buffers ----------------
+ *
+ * These are chunk-sized working buffers. Each device has a few.
+ */
+
+struct yaffs_buffer {
+ u8 *buffer;
+ int in_use;
+};
+
+/*----------------- Device ---------------------------------*/
+
+struct yaffs_param {
+ const YCHAR *name;
+
+ /*
+ * Entry parameters set up way early. Yaffs sets up the rest.
+ * The structure should be zeroed out before use so that unused
+ * and defualt values are zero.
+ */
+
+ int inband_tags; /* Use unband tags */
+ u32 total_bytes_per_chunk; /* Should be >= 512, does not need to
+ be a power of 2 */
+ int chunks_per_block; /* does not need to be a power of 2 */
+ int spare_bytes_per_chunk; /* spare area size */
+ int start_block; /* Start block we're allowed to use */
+ int end_block; /* End block we're allowed to use */
+ int n_reserved_blocks; /* Tuneable so that we can reduce
+ * reserved blocks on NOR and RAM. */
+
+ int n_caches; /* If <= 0, then short op caching is disabled,
+ * else the number of short op caches.
+ */
+ int use_nand_ecc; /* Flag to decide whether or not to use
+ * NAND driver ECC on data (yaffs1) */
+ int tags_9bytes; /* Use 9 byte tags */
+ int no_tags_ecc; /* Flag to decide whether or not to do ECC
+ * on packed tags (yaffs2) */
+
+ int is_yaffs2; /* Use yaffs2 mode on this device */
+
+ int empty_lost_n_found; /* Auto-empty lost+found directory on mount */
+
+ int refresh_period; /* How often to check for a block refresh */
+
+ /* Checkpoint control. Can be set before or after initialisation */
+ u8 skip_checkpt_rd;
+ u8 skip_checkpt_wr;
+
+ int enable_xattr; /* Enable xattribs */
+
+ /* NAND access functions (Must be set before calling YAFFS) */
+
+ int (*write_chunk_fn) (struct yaffs_dev *dev,
+ int nand_chunk, const u8 *data,
+ const struct yaffs_spare *spare);
+ int (*read_chunk_fn) (struct yaffs_dev *dev,
+ int nand_chunk, u8 *data,
+ struct yaffs_spare *spare);
+ int (*erase_fn) (struct yaffs_dev *dev, int flash_block);
+ int (*initialise_flash_fn) (struct yaffs_dev *dev);
+ int (*deinitialise_flash_fn) (struct yaffs_dev *dev);
+
+ /* yaffs2 mode functions */
+ int (*write_chunk_tags_fn) (struct yaffs_dev *dev,
+ int nand_chunk, const u8 *data,
+ const struct yaffs_ext_tags *tags);
+ int (*read_chunk_tags_fn) (struct yaffs_dev *dev,
+ int nand_chunk, u8 *data,
+ struct yaffs_ext_tags *tags);
+ int (*bad_block_fn) (struct yaffs_dev *dev, int block_no);
+ int (*query_block_fn) (struct yaffs_dev *dev, int block_no,
+ enum yaffs_block_state *state,
+ u32 *seq_number);
+
+ /* The remove_obj_fn function must be supplied by OS flavours that
+ * need it.
+ * yaffs direct uses it to implement the faster readdir.
+ * Linux uses it to protect the directory during unlocking.
+ */
+ void (*remove_obj_fn) (struct yaffs_obj *obj);
+
+ /* Callback to mark the superblock dirty */
+ void (*sb_dirty_fn) (struct yaffs_dev *dev);
+
+ /* Callback to control garbage collection. */
+ unsigned (*gc_control) (struct yaffs_dev *dev);
+
+ /* Debug control flags. Don't use unless you know what you're doing */
+ int use_header_file_size; /* Flag to determine if we should use
+ * file sizes from the header */
+ int disable_lazy_load; /* Disable lazy loading on this device */
+ int wide_tnodes_disabled; /* Set to disable wide tnodes */
+ int disable_soft_del; /* yaffs 1 only: Set to disable the use of
+ * softdeletion. */
+
+ int defered_dir_update; /* Set to defer directory updates */
+
+#ifdef CONFIG_YAFFS_AUTO_UNICODE
+ int auto_unicode;
+#endif
+ int always_check_erased; /* Force chunk erased check always on */
+
+ int disable_summary;
+
+ int max_objects; /*
+ * Set to limit the number of objects created.
+ * 0 = no limit.
+ */
+};
+
+struct yaffs_dev {
+ struct yaffs_param param;
+
+ /* Context storage. Holds extra OS specific data for this device */
+
+ void *os_context;
+ void *driver_context;
+
+ struct list_head dev_list;
+
+ /* Runtime parameters. Set up by YAFFS. */
+ int data_bytes_per_chunk;
+
+ /* Non-wide tnode stuff */
+ u16 chunk_grp_bits; /* Number of bits that need to be resolved if
+ * the tnodes are not wide enough.
+ */
+ u16 chunk_grp_size; /* == 2^^chunk_grp_bits */
+
+ /* Stuff to support wide tnodes */
+ u32 tnode_width;
+ u32 tnode_mask;
+ u32 tnode_size;
+
+ /* Stuff for figuring out file offset to chunk conversions */
+ u32 chunk_shift; /* Shift value */
+ u32 chunk_div; /* Divisor after shifting: 1 for 2^n sizes */
+ u32 chunk_mask; /* Mask to use for power-of-2 case */
+
+ int is_mounted;
+ int read_only;
+ int is_checkpointed;
+
+ /* Stuff to support block offsetting to support start block zero */
+ int internal_start_block;
+ int internal_end_block;
+ int block_offset;
+ int chunk_offset;
+
+ /* Runtime checkpointing stuff */
+ int checkpt_page_seq; /* running sequence number of checkpt pages */
+ int checkpt_byte_count;
+ int checkpt_byte_offs;
+ u8 *checkpt_buffer;
+ int checkpt_open_write;
+ int blocks_in_checkpt;
+ int checkpt_cur_chunk;
+ int checkpt_cur_block;
+ int checkpt_next_block;
+ int *checkpt_block_list;
+ int checkpt_max_blocks;
+ u32 checkpt_sum;
+ u32 checkpt_xor;
+
+ int checkpoint_blocks_required; /* Number of blocks needed to store
+ * current checkpoint set */
+
+ /* Block Info */
+ struct yaffs_block_info *block_info;
+ u8 *chunk_bits; /* bitmap of chunks in use */
+ unsigned block_info_alt:1; /* allocated using alternative alloc */
+ unsigned chunk_bits_alt:1; /* allocated using alternative alloc */
+ int chunk_bit_stride; /* Number of bytes of chunk_bits per block.
+ * Must be consistent with chunks_per_block.
+ */
+
+ int n_erased_blocks;
+ int alloc_block; /* Current block being allocated off */
+ u32 alloc_page;
+ int alloc_block_finder; /* Used to search for next allocation block */
+
+ /* Object and Tnode memory management */
+ void *allocator;
+ int n_obj;
+ int n_tnodes;
+
+ int n_hardlinks;
+
+ struct yaffs_obj_bucket obj_bucket[YAFFS_NOBJECT_BUCKETS];
+ u32 bucket_finder;
+
+ int n_free_chunks;
+
+ /* Garbage collection control */
+ u32 *gc_cleanup_list; /* objects to delete at the end of a GC. */
+ u32 n_clean_ups;
+
+ unsigned has_pending_prioritised_gc; /* We think this device might
+ have pending prioritised gcs */
+ unsigned gc_disable;
+ unsigned gc_block_finder;
+ unsigned gc_dirtiest;
+ unsigned gc_pages_in_use;
+ unsigned gc_not_done;
+ unsigned gc_block;
+ unsigned gc_chunk;
+ unsigned gc_skip;
+ struct yaffs_summary_tags *gc_sum_tags;
+
+ /* Special directories */
+ struct yaffs_obj *root_dir;
+ struct yaffs_obj *lost_n_found;
+
+ int buffered_block; /* Which block is buffered here? */
+ int doing_buffered_block_rewrite;
+
+ struct yaffs_cache *cache;
+ int cache_last_use;
+
+ /* Stuff for background deletion and unlinked files. */
+ struct yaffs_obj *unlinked_dir; /* Directory where unlinked and deleted
+ files live. */
+ struct yaffs_obj *del_dir; /* Directory where deleted objects are
+ sent to disappear. */
+ struct yaffs_obj *unlinked_deletion; /* Current file being
+ background deleted. */
+ int n_deleted_files; /* Count of files awaiting deletion; */
+ int n_unlinked_files; /* Count of unlinked files. */
+ int n_bg_deletions; /* Count of background deletions. */
+
+ /* Temporary buffer management */
+ struct yaffs_buffer temp_buffer[YAFFS_N_TEMP_BUFFERS];
+ int max_temp;
+ int temp_in_use;
+ int unmanaged_buffer_allocs;
+ int unmanaged_buffer_deallocs;
+
+ /* yaffs2 runtime stuff */
+ unsigned seq_number; /* Sequence number of currently
+ allocating block */
+ unsigned oldest_dirty_seq;
+ unsigned oldest_dirty_block;
+
+ /* Block refreshing */
+ int refresh_skip; /* A skip down counter.
+ * Refresh happens when this gets to zero. */
+
+ /* Dirty directory handling */
+ struct list_head dirty_dirs; /* List of dirty directories */
+
+ /* Summary */
+ int chunks_per_summary;
+ struct yaffs_summary_tags *sum_tags;
+
+ /* Statistics */
+ u32 n_page_writes;
+ u32 n_page_reads;
+ u32 n_erasures;
+ u32 n_erase_failures;
+ u32 n_gc_copies;
+ u32 all_gcs;
+ u32 passive_gc_count;
+ u32 oldest_dirty_gc_count;
+ u32 n_gc_blocks;
+ u32 bg_gcs;
+ u32 n_retried_writes;
+ u32 n_retired_blocks;
+ u32 n_ecc_fixed;
+ u32 n_ecc_unfixed;
+ u32 n_tags_ecc_fixed;
+ u32 n_tags_ecc_unfixed;
+ u32 n_deletions;
+ u32 n_unmarked_deletions;
+ u32 refresh_count;
+ u32 cache_hits;
+ u32 tags_used;
+ u32 summary_used;
+
+};
+
+/* The CheckpointDevice structure holds the device information that changes
+ *at runtime and must be preserved over unmount/mount cycles.
+ */
+struct yaffs_checkpt_dev {
+ int struct_type;
+ int n_erased_blocks;
+ int alloc_block; /* Current block being allocated off */
+ u32 alloc_page;
+ int n_free_chunks;
+
+ int n_deleted_files; /* Count of files awaiting deletion; */
+ int n_unlinked_files; /* Count of unlinked files. */
+ int n_bg_deletions; /* Count of background deletions. */
+
+ /* yaffs2 runtime stuff */
+ unsigned seq_number; /* Sequence number of currently
+ * allocating block */
+
+};
+
+struct yaffs_checkpt_validity {
+ int struct_type;
+ u32 magic;
+ u32 version;
+ u32 head;
+};
+
+struct yaffs_shadow_fixer {
+ int obj_id;
+ int shadowed_id;
+ struct yaffs_shadow_fixer *next;
+};
+
+/* Structure for doing xattr modifications */
+struct yaffs_xattr_mod {
+ int set; /* If 0 then this is a deletion */
+ const YCHAR *name;
+ const void *data;
+ int size;
+ int flags;
+ int result;
+};
+
+/*----------------------- YAFFS Functions -----------------------*/
+
+int yaffs_guts_initialise(struct yaffs_dev *dev);
+void yaffs_deinitialise(struct yaffs_dev *dev);
+
+int yaffs_get_n_free_chunks(struct yaffs_dev *dev);
+
+int yaffs_rename_obj(struct yaffs_obj *old_dir, const YCHAR * old_name,
+ struct yaffs_obj *new_dir, const YCHAR * new_name);
+
+int yaffs_unlinker(struct yaffs_obj *dir, const YCHAR * name);
+int yaffs_del_obj(struct yaffs_obj *obj);
+
+int yaffs_get_obj_name(struct yaffs_obj *obj, YCHAR * name, int buffer_size);
+loff_t yaffs_get_obj_length(struct yaffs_obj *obj);
+int yaffs_get_obj_inode(struct yaffs_obj *obj);
+unsigned yaffs_get_obj_type(struct yaffs_obj *obj);
+int yaffs_get_obj_link_count(struct yaffs_obj *obj);
+
+/* File operations */
+int yaffs_file_rd(struct yaffs_obj *obj, u8 * buffer, loff_t offset,
+ int n_bytes);
+int yaffs_wr_file(struct yaffs_obj *obj, const u8 * buffer, loff_t offset,
+ int n_bytes, int write_trhrough);
+int yaffs_resize_file(struct yaffs_obj *obj, loff_t new_size);
+
+struct yaffs_obj *yaffs_create_file(struct yaffs_obj *parent,
+ const YCHAR *name, u32 mode, u32 uid,
+ u32 gid);
+
+int yaffs_flush_file(struct yaffs_obj *obj, int update_time, int data_sync);
+
+/* Flushing and checkpointing */
+void yaffs_flush_whole_cache(struct yaffs_dev *dev);
+
+int yaffs_checkpoint_save(struct yaffs_dev *dev);
+int yaffs_checkpoint_restore(struct yaffs_dev *dev);
+
+/* Directory operations */
+struct yaffs_obj *yaffs_create_dir(struct yaffs_obj *parent, const YCHAR *name,
+ u32 mode, u32 uid, u32 gid);
+struct yaffs_obj *yaffs_find_by_name(struct yaffs_obj *the_dir,
+ const YCHAR *name);
+struct yaffs_obj *yaffs_find_by_number(struct yaffs_dev *dev, u32 number);
+
+/* Link operations */
+struct yaffs_obj *yaffs_link_obj(struct yaffs_obj *parent, const YCHAR *name,
+ struct yaffs_obj *equiv_obj);
+
+struct yaffs_obj *yaffs_get_equivalent_obj(struct yaffs_obj *obj);
+
+/* Symlink operations */
+struct yaffs_obj *yaffs_create_symlink(struct yaffs_obj *parent,
+ const YCHAR *name, u32 mode, u32 uid,
+ u32 gid, const YCHAR *alias);
+YCHAR *yaffs_get_symlink_alias(struct yaffs_obj *obj);
+
+/* Special inodes (fifos, sockets and devices) */
+struct yaffs_obj *yaffs_create_special(struct yaffs_obj *parent,
+ const YCHAR *name, u32 mode, u32 uid,
+ u32 gid, u32 rdev);
+
+int yaffs_set_xattrib(struct yaffs_obj *obj, const YCHAR *name,
+ const void *value, int size, int flags);
+int yaffs_get_xattrib(struct yaffs_obj *obj, const YCHAR *name, void *value,
+ int size);
+int yaffs_list_xattrib(struct yaffs_obj *obj, char *buffer, int size);
+int yaffs_remove_xattrib(struct yaffs_obj *obj, const YCHAR *name);
+
+/* Special directories */
+struct yaffs_obj *yaffs_root(struct yaffs_dev *dev);
+struct yaffs_obj *yaffs_lost_n_found(struct yaffs_dev *dev);
+
+void yaffs_handle_defered_free(struct yaffs_obj *obj);
+
+void yaffs_update_dirty_dirs(struct yaffs_dev *dev);
+
+int yaffs_bg_gc(struct yaffs_dev *dev, unsigned urgency);
+
+/* Debug dump */
+int yaffs_dump_obj(struct yaffs_obj *obj);
+
+void yaffs_guts_test(struct yaffs_dev *dev);
+
+/* A few useful functions to be used within the core files*/
+void yaffs_chunk_del(struct yaffs_dev *dev, int chunk_id, int mark_flash,
+ int lyn);
+int yaffs_check_ff(u8 *buffer, int n_bytes);
+void yaffs_handle_chunk_error(struct yaffs_dev *dev,
+ struct yaffs_block_info *bi);
+
+u8 *yaffs_get_temp_buffer(struct yaffs_dev *dev);
+void yaffs_release_temp_buffer(struct yaffs_dev *dev, u8 *buffer);
+
+struct yaffs_obj *yaffs_find_or_create_by_number(struct yaffs_dev *dev,
+ int number,
+ enum yaffs_obj_type type);
+int yaffs_put_chunk_in_file(struct yaffs_obj *in, int inode_chunk,
+ int nand_chunk, int in_scan);
+void yaffs_set_obj_name(struct yaffs_obj *obj, const YCHAR *name);
+void yaffs_set_obj_name_from_oh(struct yaffs_obj *obj,
+ const struct yaffs_obj_hdr *oh);
+void yaffs_add_obj_to_dir(struct yaffs_obj *directory, struct yaffs_obj *obj);
+YCHAR *yaffs_clone_str(const YCHAR *str);
+void yaffs_link_fixup(struct yaffs_dev *dev, struct list_head *hard_list);
+void yaffs_block_became_dirty(struct yaffs_dev *dev, int block_no);
+int yaffs_update_oh(struct yaffs_obj *in, const YCHAR *name,
+ int force, int is_shrink, int shadows,
+ struct yaffs_xattr_mod *xop);
+void yaffs_handle_shadowed_obj(struct yaffs_dev *dev, int obj_id,
+ int backward_scanning);
+int yaffs_check_alloc_available(struct yaffs_dev *dev, int n_chunks);
+struct yaffs_tnode *yaffs_get_tnode(struct yaffs_dev *dev);
+struct yaffs_tnode *yaffs_add_find_tnode_0(struct yaffs_dev *dev,
+ struct yaffs_file_var *file_struct,
+ u32 chunk_id,
+ struct yaffs_tnode *passed_tn);
+
+int yaffs_do_file_wr(struct yaffs_obj *in, const u8 *buffer, loff_t offset,
+ int n_bytes, int write_trhrough);
+void yaffs_resize_file_down(struct yaffs_obj *obj, loff_t new_size);
+void yaffs_skip_rest_of_block(struct yaffs_dev *dev);
+
+int yaffs_count_free_chunks(struct yaffs_dev *dev);
+
+struct yaffs_tnode *yaffs_find_tnode_0(struct yaffs_dev *dev,
+ struct yaffs_file_var *file_struct,
+ u32 chunk_id);
+
+u32 yaffs_get_group_base(struct yaffs_dev *dev, struct yaffs_tnode *tn,
+ unsigned pos);
+
+int yaffs_is_non_empty_dir(struct yaffs_obj *obj);
+
+void yaffs_addr_to_chunk(struct yaffs_dev *dev, loff_t addr,
+ int *chunk_out, u32 *offset_out);
+/*
+ * Marshalling functions to get loff_t file sizes into aand out of
+ * object headers.
+ */
+void yaffs_oh_size_load(struct yaffs_obj_hdr *oh, loff_t fsize);
+loff_t yaffs_oh_to_size(struct yaffs_obj_hdr *oh);
+loff_t yaffs_max_file_size(struct yaffs_dev *dev);
+
+
+#endif
diff --git a/roms/u-boot/fs/yaffs2/yaffs_mtdif.c b/roms/u-boot/fs/yaffs2/yaffs_mtdif.c
new file mode 100644
index 000000000..d338f9aa9
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_mtdif.c
@@ -0,0 +1,165 @@
+/*
+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2007 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/* XXX U-BOOT XXX */
+#include <common.h>
+
+#include "yportenv.h"
+
+
+#include "yaffs_mtdif.h"
+
+#include <linux/mtd/mtd.h>
+#include <linux/types.h>
+#include <linux/time.h>
+#include <linux/mtd/rawnand.h>
+
+
+static inline void translate_spare2oob(const struct yaffs_spare *spare, u8 *oob)
+{
+ oob[0] = spare->tb0;
+ oob[1] = spare->tb1;
+ oob[2] = spare->tb2;
+ oob[3] = spare->tb3;
+ oob[4] = spare->tb4;
+ oob[5] = spare->tb5 & 0x3f;
+ oob[5] |= spare->block_status == 'Y' ? 0 : 0x80;
+ oob[5] |= spare->page_status == 0 ? 0 : 0x40;
+ oob[6] = spare->tb6;
+ oob[7] = spare->tb7;
+}
+
+static inline void translate_oob2spare(struct yaffs_spare *spare, u8 *oob)
+{
+ struct yaffs_nand_spare *nspare = (struct yaffs_nand_spare *)spare;
+ spare->tb0 = oob[0];
+ spare->tb1 = oob[1];
+ spare->tb2 = oob[2];
+ spare->tb3 = oob[3];
+ spare->tb4 = oob[4];
+ spare->tb5 = oob[5] == 0xff ? 0xff : oob[5] & 0x3f;
+ spare->block_status = oob[5] & 0x80 ? 0xff : 'Y';
+ spare->page_status = oob[5] & 0x40 ? 0xff : 0;
+ spare->ecc1[0] = spare->ecc1[1] = spare->ecc1[2] = 0xff;
+ spare->tb6 = oob[6];
+ spare->tb7 = oob[7];
+ spare->ecc2[0] = spare->ecc2[1] = spare->ecc2[2] = 0xff;
+
+ nspare->eccres1 = nspare->eccres2 = 0; /* FIXME */
+}
+
+
+int nandmtd_WriteChunkToNAND(struct yaffs_dev *dev, int chunkInNAND,
+ const u8 *data, const struct yaffs_spare *spare)
+{
+ struct mtd_info *mtd = (struct mtd_info *)(dev->driver_context);
+ struct mtd_oob_ops ops;
+ size_t dummy;
+ int retval = 0;
+ loff_t addr = ((loff_t) chunkInNAND) * dev->data_bytes_per_chunk;
+ u8 spareAsBytes[8]; /* OOB */
+
+ if (data && !spare)
+ retval = mtd_write(mtd, addr, dev->data_bytes_per_chunk,
+ &dummy, data);
+ else if (spare) {
+ if (dev->param.use_nand_ecc) {
+ translate_spare2oob(spare, spareAsBytes);
+ ops.mode = MTD_OPS_AUTO_OOB;
+ ops.ooblen = 8; /* temp hack */
+ } else {
+ ops.mode = MTD_OPS_RAW;
+ ops.ooblen = YAFFS_BYTES_PER_SPARE;
+ }
+ ops.len = data ? dev->data_bytes_per_chunk : ops.ooblen;
+ ops.datbuf = (u8 *)data;
+ ops.ooboffs = 0;
+ ops.oobbuf = spareAsBytes;
+ retval = mtd_write_oob(mtd, addr, &ops);
+ }
+
+ if (retval == 0)
+ return YAFFS_OK;
+ else
+ return YAFFS_FAIL;
+}
+
+int nandmtd_ReadChunkFromNAND(struct yaffs_dev *dev, int chunkInNAND, u8 *data,
+ struct yaffs_spare *spare)
+{
+ struct mtd_info *mtd = (struct mtd_info *)(dev->driver_context);
+ struct mtd_oob_ops ops;
+ size_t dummy;
+ int retval = 0;
+
+ loff_t addr = ((loff_t) chunkInNAND) * dev->data_bytes_per_chunk;
+ u8 spareAsBytes[8]; /* OOB */
+
+ if (data && !spare)
+ retval = mtd_read(mtd, addr, dev->data_bytes_per_chunk,
+ &dummy, data);
+ else if (spare) {
+ if (dev->param.use_nand_ecc) {
+ ops.mode = MTD_OPS_AUTO_OOB;
+ ops.ooblen = 8; /* temp hack */
+ } else {
+ ops.mode = MTD_OPS_RAW;
+ ops.ooblen = YAFFS_BYTES_PER_SPARE;
+ }
+ ops.len = data ? dev->data_bytes_per_chunk : ops.ooblen;
+ ops.datbuf = data;
+ ops.ooboffs = 0;
+ ops.oobbuf = spareAsBytes;
+ retval = mtd_read_oob(mtd, addr, &ops);
+ if (dev->param.use_nand_ecc)
+ translate_oob2spare(spare, spareAsBytes);
+ }
+
+ if (retval == 0)
+ return YAFFS_OK;
+ else
+ return YAFFS_FAIL;
+}
+
+int nandmtd_EraseBlockInNAND(struct yaffs_dev *dev, int blockNumber)
+{
+ struct mtd_info *mtd = (struct mtd_info *)(dev->driver_context);
+ __u32 addr =
+ ((loff_t) blockNumber) * dev->data_bytes_per_chunk
+ * dev->param.chunks_per_block;
+ struct erase_info ei;
+ int retval = 0;
+
+ ei.mtd = mtd;
+ ei.addr = addr;
+ ei.len = dev->data_bytes_per_chunk * dev->param.chunks_per_block;
+ ei.time = 1000;
+ ei.retries = 2;
+ ei.callback = NULL;
+ ei.priv = (u_long) dev;
+
+ /* Todo finish off the ei if required */
+
+
+ retval = mtd_erase(mtd, &ei);
+
+ if (retval == 0)
+ return YAFFS_OK;
+ else
+ return YAFFS_FAIL;
+}
+
+int nandmtd_InitialiseNAND(struct yaffs_dev *dev)
+{
+ return YAFFS_OK;
+}
diff --git a/roms/u-boot/fs/yaffs2/yaffs_mtdif.h b/roms/u-boot/fs/yaffs2/yaffs_mtdif.h
new file mode 100644
index 000000000..1a125823b
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_mtdif.h
@@ -0,0 +1,27 @@
+/*
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2007 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ */
+
+#ifndef __YAFFS_MTDIF_H__
+#define __YAFFS_MTDIF_H__
+
+#include "yaffs_guts.h"
+
+int nandmtd_WriteChunkToNAND(struct yaffs_dev *dev, int chunkInNAND,
+ const u8 *data, const struct yaffs_spare *spare);
+int nandmtd_ReadChunkFromNAND(struct yaffs_dev *dev, int chunkInNAND, u8 *data,
+ struct yaffs_spare *spare);
+int nandmtd_EraseBlockInNAND(struct yaffs_dev *dev, int blockNumber);
+int nandmtd_InitialiseNAND(struct yaffs_dev *dev);
+#endif
diff --git a/roms/u-boot/fs/yaffs2/yaffs_mtdif2.c b/roms/u-boot/fs/yaffs2/yaffs_mtdif2.c
new file mode 100644
index 000000000..81a4d964f
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_mtdif2.c
@@ -0,0 +1,233 @@
+/*
+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2007 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/* mtd interface for YAFFS2 */
+
+/* XXX U-BOOT XXX */
+#include <common.h>
+#include <linux/bug.h>
+#include <linux/errno.h>
+
+#include "yportenv.h"
+#include "yaffs_trace.h"
+
+#include "yaffs_mtdif2.h"
+
+#include <linux/mtd/mtd.h>
+#include <linux/types.h>
+#include <linux/time.h>
+
+#include "yaffs_trace.h"
+#include "yaffs_packedtags2.h"
+
+#define yaffs_dev_to_mtd(dev) ((struct mtd_info *)((dev)->driver_context))
+#define yaffs_dev_to_lc(dev) ((struct yaffs_linux_context *)((dev)->os_context))
+
+
+/* NB For use with inband tags....
+ * We assume that the data buffer is of size total_bytes_per_chunk so
+ * that we can also use it to load the tags.
+ */
+int nandmtd2_write_chunk_tags(struct yaffs_dev *dev, int nand_chunk,
+ const u8 *data,
+ const struct yaffs_ext_tags *tags)
+{
+ struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
+ struct mtd_oob_ops ops;
+
+ int retval = 0;
+ loff_t addr;
+
+ struct yaffs_packed_tags2 pt;
+
+ int packed_tags_size =
+ dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt);
+ void *packed_tags_ptr =
+ dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt;
+
+ yaffs_trace(YAFFS_TRACE_MTD,
+ "nandmtd2_write_chunk_tags chunk %d data %p tags %p",
+ nand_chunk, data, tags);
+
+ addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk;
+
+ /* For yaffs2 writing there must be both data and tags.
+ * If we're using inband tags, then the tags are stuffed into
+ * the end of the data buffer.
+ */
+ if (!data || !tags)
+ BUG();
+ else if (dev->param.inband_tags) {
+ struct yaffs_packed_tags2_tags_only *pt2tp;
+ pt2tp =
+ (struct yaffs_packed_tags2_tags_only *)(data +
+ dev->
+ data_bytes_per_chunk);
+ yaffs_pack_tags2_tags_only(pt2tp, tags);
+ } else {
+ yaffs_pack_tags2(&pt, tags, !dev->param.no_tags_ecc);
+ }
+
+ ops.mode = MTD_OPS_AUTO_OOB;
+ ops.ooblen = (dev->param.inband_tags) ? 0 : packed_tags_size;
+ ops.len = dev->param.total_bytes_per_chunk;
+ ops.ooboffs = 0;
+ ops.datbuf = (u8 *) data;
+ ops.oobbuf = (dev->param.inband_tags) ? NULL : packed_tags_ptr;
+ retval = mtd_write_oob(mtd, addr, &ops);
+
+ if (retval == 0)
+ return YAFFS_OK;
+ else
+ return YAFFS_FAIL;
+}
+
+int nandmtd2_read_chunk_tags(struct yaffs_dev *dev, int nand_chunk,
+ u8 *data, struct yaffs_ext_tags *tags)
+{
+ struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
+ u8 local_spare[128];
+ struct mtd_oob_ops ops;
+ size_t dummy;
+ int retval = 0;
+ int local_data = 0;
+ struct yaffs_packed_tags2 pt;
+ loff_t addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk;
+ int packed_tags_size =
+ dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt);
+ void *packed_tags_ptr =
+ dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt;
+
+ yaffs_trace(YAFFS_TRACE_MTD,
+ "nandmtd2_read_chunk_tags chunk %d data %p tags %p",
+ nand_chunk, data, tags);
+
+ if (dev->param.inband_tags) {
+
+ if (!data) {
+ local_data = 1;
+ data = yaffs_get_temp_buffer(dev);
+ }
+
+ }
+
+ if (dev->param.inband_tags || (data && !tags))
+ retval = mtd_read(mtd, addr, dev->param.total_bytes_per_chunk,
+ &dummy, data);
+ else if (tags) {
+ ops.mode = MTD_OPS_AUTO_OOB;
+ ops.ooblen = packed_tags_size;
+ ops.len = data ? dev->data_bytes_per_chunk : packed_tags_size;
+ ops.ooboffs = 0;
+ ops.datbuf = data;
+ ops.oobbuf = local_spare;
+ retval = mtd_read_oob(mtd, addr, &ops);
+ }
+
+ if (dev->param.inband_tags) {
+ if (tags) {
+ struct yaffs_packed_tags2_tags_only *pt2tp;
+ pt2tp =
+ (struct yaffs_packed_tags2_tags_only *)
+ &data[dev->data_bytes_per_chunk];
+ yaffs_unpack_tags2_tags_only(tags, pt2tp);
+ }
+ } else {
+ if (tags) {
+ memcpy(packed_tags_ptr,
+ local_spare,
+ packed_tags_size);
+ yaffs_unpack_tags2(tags, &pt, !dev->param.no_tags_ecc);
+ }
+ }
+
+ if (local_data)
+ yaffs_release_temp_buffer(dev, data);
+
+ if (tags && retval == -EBADMSG
+ && tags->ecc_result == YAFFS_ECC_RESULT_NO_ERROR) {
+ tags->ecc_result = YAFFS_ECC_RESULT_UNFIXED;
+ dev->n_ecc_unfixed++;
+ }
+ if (tags && retval == -EUCLEAN
+ && tags->ecc_result == YAFFS_ECC_RESULT_NO_ERROR) {
+ tags->ecc_result = YAFFS_ECC_RESULT_FIXED;
+ dev->n_ecc_fixed++;
+ }
+ if (retval == 0)
+ return YAFFS_OK;
+ else
+ return YAFFS_FAIL;
+}
+
+
+int nandmtd2_MarkNANDBlockBad(struct yaffs_dev *dev, int blockNo)
+{
+ struct mtd_info *mtd = (struct mtd_info *)(dev->driver_context);
+ int retval;
+
+ yaffs_trace(YAFFS_TRACE_MTD,
+ "nandmtd2_MarkNANDBlockBad %d", blockNo);
+
+ retval =
+ mtd_block_markbad(mtd,
+ blockNo * dev->param.chunks_per_block *
+ dev->data_bytes_per_chunk);
+
+ if (retval == 0)
+ return YAFFS_OK;
+ else
+ return YAFFS_FAIL;
+
+}
+
+int nandmtd2_QueryNANDBlock(struct yaffs_dev *dev, int blockNo,
+ enum yaffs_block_state *state, u32 *sequenceNumber)
+{
+ struct mtd_info *mtd = (struct mtd_info *)(dev->driver_context);
+ int retval;
+
+ yaffs_trace(YAFFS_TRACE_MTD, "nandmtd2_QueryNANDBlock %d", blockNo);
+ retval =
+ mtd_block_isbad(mtd,
+ blockNo * dev->param.chunks_per_block *
+ dev->data_bytes_per_chunk);
+
+ if (retval) {
+ yaffs_trace(YAFFS_TRACE_MTD, "block is bad");
+
+ *state = YAFFS_BLOCK_STATE_DEAD;
+ *sequenceNumber = 0;
+ } else {
+ struct yaffs_ext_tags t;
+ nandmtd2_read_chunk_tags(dev,
+ blockNo *
+ dev->param.chunks_per_block, NULL,
+ &t);
+
+ if (t.chunk_used) {
+ *sequenceNumber = t.seq_number;
+ *state = YAFFS_BLOCK_STATE_NEEDS_SCAN;
+ } else {
+ *sequenceNumber = 0;
+ *state = YAFFS_BLOCK_STATE_EMPTY;
+ }
+ }
+ yaffs_trace(YAFFS_TRACE_MTD, "block is bad seq %d state %d",
+ *sequenceNumber, *state);
+
+ if (retval == 0)
+ return YAFFS_OK;
+ else
+ return YAFFS_FAIL;
+}
diff --git a/roms/u-boot/fs/yaffs2/yaffs_mtdif2.h b/roms/u-boot/fs/yaffs2/yaffs_mtdif2.h
new file mode 100644
index 000000000..62be17363
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_mtdif2.h
@@ -0,0 +1,30 @@
+/*
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2007 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ */
+
+#ifndef __YAFFS_MTDIF2_H__
+#define __YAFFS_MTDIF2_H__
+
+#include "yaffs_guts.h"
+
+int nandmtd2_write_chunk_tags(struct yaffs_dev *dev, int chunkInNAND,
+ const u8 *data,
+ const struct yaffs_ext_tags *tags);
+int nandmtd2_read_chunk_tags(struct yaffs_dev *dev, int chunkInNAND,
+ u8 *data, struct yaffs_ext_tags *tags);
+int nandmtd2_MarkNANDBlockBad(struct yaffs_dev *dev, int blockNo);
+int nandmtd2_QueryNANDBlock(struct yaffs_dev *dev, int blockNo,
+ enum yaffs_block_state *state, u32 *sequenceNumber);
+
+#endif
diff --git a/roms/u-boot/fs/yaffs2/yaffs_nameval.c b/roms/u-boot/fs/yaffs2/yaffs_nameval.c
new file mode 100644
index 000000000..a20a3e47c
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_nameval.c
@@ -0,0 +1,208 @@
+/*
+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/*
+ * This simple implementation of a name-value store assumes a small number of
+* values and fits into a small finite buffer.
+ *
+ * Each attribute is stored as a record:
+ * sizeof(int) bytes record size.
+ * yaffs_strnlen+1 bytes name null terminated.
+ * nbytes value.
+ * ----------
+ * total size stored in record size
+ *
+ * This code has not been tested with unicode yet.
+ */
+
+#include "yaffs_nameval.h"
+
+#include "yportenv.h"
+
+static int nval_find(const char *xb, int xb_size, const YCHAR *name,
+ int *exist_size)
+{
+ int pos = 0;
+ int size;
+
+ memcpy(&size, xb, sizeof(int));
+ while (size > 0 && (size < xb_size) && (pos + size < xb_size)) {
+ if (!yaffs_strncmp((YCHAR *) (xb + pos + sizeof(int)),
+ name, size)) {
+ if (exist_size)
+ *exist_size = size;
+ return pos;
+ }
+ pos += size;
+ if (pos < xb_size - sizeof(int))
+ memcpy(&size, xb + pos, sizeof(int));
+ else
+ size = 0;
+ }
+ if (exist_size)
+ *exist_size = 0;
+ return -ENODATA;
+}
+
+static int nval_used(const char *xb, int xb_size)
+{
+ int pos = 0;
+ int size;
+
+ memcpy(&size, xb + pos, sizeof(int));
+ while (size > 0 && (size < xb_size) && (pos + size < xb_size)) {
+ pos += size;
+ if (pos < xb_size - sizeof(int))
+ memcpy(&size, xb + pos, sizeof(int));
+ else
+ size = 0;
+ }
+ return pos;
+}
+
+int nval_del(char *xb, int xb_size, const YCHAR *name)
+{
+ int pos = nval_find(xb, xb_size, name, NULL);
+ int size;
+
+ if (pos < 0 || pos >= xb_size)
+ return -ENODATA;
+
+ /* Find size, shift rest over this record,
+ * then zero out the rest of buffer */
+ memcpy(&size, xb + pos, sizeof(int));
+ memcpy(xb + pos, xb + pos + size, xb_size - (pos + size));
+ memset(xb + (xb_size - size), 0, size);
+ return 0;
+}
+
+int nval_set(char *xb, int xb_size, const YCHAR *name, const char *buf,
+ int bsize, int flags)
+{
+ int pos;
+ int namelen = yaffs_strnlen(name, xb_size);
+ int reclen;
+ int size_exist = 0;
+ int space;
+ int start;
+
+ pos = nval_find(xb, xb_size, name, &size_exist);
+
+ if (flags & XATTR_CREATE && pos >= 0)
+ return -EEXIST;
+ if (flags & XATTR_REPLACE && pos < 0)
+ return -ENODATA;
+
+ start = nval_used(xb, xb_size);
+ space = xb_size - start + size_exist;
+
+ reclen = (sizeof(int) + namelen + 1 + bsize);
+
+ if (reclen > space)
+ return -ENOSPC;
+
+ if (pos >= 0) {
+ nval_del(xb, xb_size, name);
+ start = nval_used(xb, xb_size);
+ }
+
+ pos = start;
+
+ memcpy(xb + pos, &reclen, sizeof(int));
+ pos += sizeof(int);
+ yaffs_strncpy((YCHAR *) (xb + pos), name, reclen);
+ pos += (namelen + 1);
+ memcpy(xb + pos, buf, bsize);
+ return 0;
+}
+
+int nval_get(const char *xb, int xb_size, const YCHAR * name, char *buf,
+ int bsize)
+{
+ int pos = nval_find(xb, xb_size, name, NULL);
+ int size;
+
+ if (pos >= 0 && pos < xb_size) {
+
+ memcpy(&size, xb + pos, sizeof(int));
+ pos += sizeof(int); /* advance past record length */
+ size -= sizeof(int);
+
+ /* Advance over name string */
+ while (xb[pos] && size > 0 && pos < xb_size) {
+ pos++;
+ size--;
+ }
+ /*Advance over NUL */
+ pos++;
+ size--;
+
+ /* If bsize is zero then this is a size query.
+ * Return the size, but don't copy.
+ */
+ if (!bsize)
+ return size;
+
+ if (size <= bsize) {
+ memcpy(buf, xb + pos, size);
+ return size;
+ }
+ }
+ if (pos >= 0)
+ return -ERANGE;
+
+ return -ENODATA;
+}
+
+int nval_list(const char *xb, int xb_size, char *buf, int bsize)
+{
+ int pos = 0;
+ int size;
+ int name_len;
+ int ncopied = 0;
+ int filled = 0;
+
+ memcpy(&size, xb + pos, sizeof(int));
+ while (size > sizeof(int) &&
+ size <= xb_size &&
+ (pos + size) < xb_size &&
+ !filled) {
+ pos += sizeof(int);
+ size -= sizeof(int);
+ name_len = yaffs_strnlen((YCHAR *) (xb + pos), size);
+ if (ncopied + name_len + 1 < bsize) {
+ memcpy(buf, xb + pos, name_len * sizeof(YCHAR));
+ buf += name_len;
+ *buf = '\0';
+ buf++;
+ if (sizeof(YCHAR) > 1) {
+ *buf = '\0';
+ buf++;
+ }
+ ncopied += (name_len + 1);
+ } else {
+ filled = 1;
+ }
+ pos += size;
+ if (pos < xb_size - sizeof(int))
+ memcpy(&size, xb + pos, sizeof(int));
+ else
+ size = 0;
+ }
+ return ncopied;
+}
+
+int nval_hasvalues(const char *xb, int xb_size)
+{
+ return nval_used(xb, xb_size) > 0;
+}
diff --git a/roms/u-boot/fs/yaffs2/yaffs_nameval.h b/roms/u-boot/fs/yaffs2/yaffs_nameval.h
new file mode 100644
index 000000000..951e64f87
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_nameval.h
@@ -0,0 +1,28 @@
+/*
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ */
+
+#ifndef __NAMEVAL_H__
+#define __NAMEVAL_H__
+
+#include "yportenv.h"
+
+int nval_del(char *xb, int xb_size, const YCHAR * name);
+int nval_set(char *xb, int xb_size, const YCHAR * name, const char *buf,
+ int bsize, int flags);
+int nval_get(const char *xb, int xb_size, const YCHAR * name, char *buf,
+ int bsize);
+int nval_list(const char *xb, int xb_size, char *buf, int bsize);
+int nval_hasvalues(const char *xb, int xb_size);
+#endif
diff --git a/roms/u-boot/fs/yaffs2/yaffs_nand.c b/roms/u-boot/fs/yaffs2/yaffs_nand.c
new file mode 100644
index 000000000..165d01004
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_nand.c
@@ -0,0 +1,120 @@
+/*
+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "yaffs_nand.h"
+#include "yaffs_tagscompat.h"
+
+#include "yaffs_getblockinfo.h"
+#include "yaffs_summary.h"
+
+int yaffs_rd_chunk_tags_nand(struct yaffs_dev *dev, int nand_chunk,
+ u8 *buffer, struct yaffs_ext_tags *tags)
+{
+ int result;
+ struct yaffs_ext_tags local_tags;
+ int flash_chunk = nand_chunk - dev->chunk_offset;
+
+ dev->n_page_reads++;
+
+ /* If there are no tags provided use local tags. */
+ if (!tags)
+ tags = &local_tags;
+
+ if (dev->param.read_chunk_tags_fn)
+ result =
+ dev->param.read_chunk_tags_fn(dev, flash_chunk, buffer,
+ tags);
+ else
+ result = yaffs_tags_compat_rd(dev,
+ flash_chunk, buffer, tags);
+ if (tags && tags->ecc_result > YAFFS_ECC_RESULT_NO_ERROR) {
+
+ struct yaffs_block_info *bi;
+ bi = yaffs_get_block_info(dev,
+ nand_chunk /
+ dev->param.chunks_per_block);
+ yaffs_handle_chunk_error(dev, bi);
+ }
+ return result;
+}
+
+int yaffs_wr_chunk_tags_nand(struct yaffs_dev *dev,
+ int nand_chunk,
+ const u8 *buffer, struct yaffs_ext_tags *tags)
+{
+ int result;
+ int flash_chunk = nand_chunk - dev->chunk_offset;
+
+ dev->n_page_writes++;
+
+ if (tags) {
+ tags->seq_number = dev->seq_number;
+ tags->chunk_used = 1;
+ yaffs_trace(YAFFS_TRACE_WRITE,
+ "Writing chunk %d tags %d %d",
+ nand_chunk, tags->obj_id, tags->chunk_id);
+ } else {
+ yaffs_trace(YAFFS_TRACE_ERROR, "Writing with no tags");
+ BUG();
+ return YAFFS_FAIL;
+ }
+
+ if (dev->param.write_chunk_tags_fn)
+ result = dev->param.write_chunk_tags_fn(dev, flash_chunk,
+ buffer, tags);
+ else
+ result = yaffs_tags_compat_wr(dev, flash_chunk, buffer, tags);
+
+ yaffs_summary_add(dev, tags, nand_chunk);
+
+ return result;
+}
+
+int yaffs_mark_bad(struct yaffs_dev *dev, int block_no)
+{
+ block_no -= dev->block_offset;
+ if (dev->param.bad_block_fn)
+ return dev->param.bad_block_fn(dev, block_no);
+
+ return yaffs_tags_compat_mark_bad(dev, block_no);
+}
+
+int yaffs_query_init_block_state(struct yaffs_dev *dev,
+ int block_no,
+ enum yaffs_block_state *state,
+ u32 *seq_number)
+{
+ block_no -= dev->block_offset;
+ if (dev->param.query_block_fn)
+ return dev->param.query_block_fn(dev, block_no, state,
+ seq_number);
+
+ return yaffs_tags_compat_query_block(dev, block_no, state, seq_number);
+}
+
+int yaffs_erase_block(struct yaffs_dev *dev, int flash_block)
+{
+ int result;
+
+ flash_block -= dev->block_offset;
+ dev->n_erasures++;
+ result = dev->param.erase_fn(dev, flash_block);
+ return result;
+}
+
+int yaffs_init_nand(struct yaffs_dev *dev)
+{
+ if (dev->param.initialise_flash_fn)
+ return dev->param.initialise_flash_fn(dev);
+ return YAFFS_OK;
+}
diff --git a/roms/u-boot/fs/yaffs2/yaffs_nand.h b/roms/u-boot/fs/yaffs2/yaffs_nand.h
new file mode 100644
index 000000000..71346627f
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_nand.h
@@ -0,0 +1,38 @@
+/*
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ */
+
+#ifndef __YAFFS_NAND_H__
+#define __YAFFS_NAND_H__
+#include "yaffs_guts.h"
+
+int yaffs_rd_chunk_tags_nand(struct yaffs_dev *dev, int nand_chunk,
+ u8 *buffer, struct yaffs_ext_tags *tags);
+
+int yaffs_wr_chunk_tags_nand(struct yaffs_dev *dev,
+ int nand_chunk,
+ const u8 *buffer, struct yaffs_ext_tags *tags);
+
+int yaffs_mark_bad(struct yaffs_dev *dev, int block_no);
+
+int yaffs_query_init_block_state(struct yaffs_dev *dev,
+ int block_no,
+ enum yaffs_block_state *state,
+ unsigned *seq_number);
+
+int yaffs_erase_block(struct yaffs_dev *dev, int flash_block);
+
+int yaffs_init_nand(struct yaffs_dev *dev);
+
+#endif
diff --git a/roms/u-boot/fs/yaffs2/yaffs_nandemul2k.h b/roms/u-boot/fs/yaffs2/yaffs_nandemul2k.h
new file mode 100644
index 000000000..cb0c4e64d
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_nandemul2k.h
@@ -0,0 +1,39 @@
+/*
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ */
+
+/* Interface to emulated NAND functions (2k page size) */
+
+#ifndef __YAFFS_NANDEMUL2K_H__
+#define __YAFFS_NANDEMUL2K_H__
+
+#include "yaffs_guts.h"
+
+int nandemul2k_WriteChunkWithTagsToNAND(struct yaffs_dev *dev,
+ int nand_chunk, const u8 *data,
+ const struct yaffs_ext_tags *tags);
+int nandemul2k_ReadChunkWithTagsFromNAND(struct yaffs_dev *dev,
+ int nand_chunk, u8 *data,
+ struct yaffs_ext_tags *tags);
+int nandemul2k_MarkNANDBlockBad(struct yaffs_dev *dev, int block_no);
+int nandemul2k_QueryNANDBlock(struct yaffs_dev *dev, int block_no,
+ enum yaffs_block_state *state, u32 *seq_number);
+int nandemul2k_EraseBlockInNAND(struct yaffs_dev *dev,
+ int flash_block);
+int nandemul2k_InitialiseNAND(struct yaffs_dev *dev);
+int nandemul2k_GetBytesPerChunk(void);
+int nandemul2k_GetChunksPerBlock(void);
+int nandemul2k_GetNumberOfBlocks(void);
+
+#endif
diff --git a/roms/u-boot/fs/yaffs2/yaffs_nandif.c b/roms/u-boot/fs/yaffs2/yaffs_nandif.c
new file mode 100644
index 000000000..ee5a17206
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_nandif.c
@@ -0,0 +1,252 @@
+/*
+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "yportenv.h"
+#include "yaffs_guts.h"
+#include <malloc.h>
+
+
+#include "yaffs_nandif.h"
+#include "yaffs_packedtags2.h"
+
+#include "yramsim.h"
+
+#include "yaffs_trace.h"
+#include "yaffsfs.h"
+
+
+/* NB For use with inband tags....
+ * We assume that the data buffer is of size totalBytersPerChunk so that
+ * we can also use it to load the tags.
+ */
+int ynandif_WriteChunkWithTagsToNAND(struct yaffs_dev *dev, int nand_chunk,
+ const u8 *data,
+ const struct yaffs_ext_tags *tags)
+{
+
+ int retval = 0;
+ struct yaffs_packed_tags2 pt;
+ void *spare;
+ unsigned spareSize = 0;
+ struct ynandif_Geometry *geometry = (struct ynandif_Geometry *)(dev->driver_context);
+
+ yaffs_trace(YAFFS_TRACE_MTD,
+ "nandmtd2_WriteChunkWithTagsToNAND chunk %d data %p tags %p",
+ nand_chunk, data, tags);
+
+
+ /* For yaffs2 writing there must be both data and tags.
+ * If we're using inband tags, then the tags are stuffed into
+ * the end of the data buffer.
+ */
+
+ if (dev->param.inband_tags) {
+ struct yaffs_packed_tags2_tags_only *pt2tp;
+
+ pt2tp = (struct yaffs_packed_tags2_tags_only *)
+ (data + dev->data_bytes_per_chunk);
+ yaffs_pack_tags2_tags_only(pt2tp, tags);
+ spare = NULL;
+ spareSize = 0;
+ } else {
+ yaffs_pack_tags2(&pt, tags, !dev->param.no_tags_ecc);
+ spare = &pt;
+ spareSize = sizeof(struct yaffs_packed_tags2);
+ }
+
+ retval = geometry->writeChunk(dev, nand_chunk,
+ data, dev->param.total_bytes_per_chunk,
+ spare, spareSize);
+
+ return retval;
+}
+
+int ynandif_ReadChunkWithTagsFromNAND(struct yaffs_dev *dev, int nand_chunk,
+ u8 *data, struct yaffs_ext_tags *tags)
+{
+ struct yaffs_packed_tags2 pt;
+ int localData = 0;
+ void *spare = NULL;
+ unsigned spareSize;
+ int retval = 0;
+ int eccStatus; /* 0 = ok, 1 = fixed, -1 = unfixed */
+ struct ynandif_Geometry *geometry = (struct ynandif_Geometry *)(dev->driver_context);
+
+ yaffs_trace(YAFFS_TRACE_MTD,
+ "nandmtd2_ReadChunkWithTagsFromNAND chunk %d data %p tags %p",
+ nand_chunk, data, tags);
+
+ if (!tags) {
+ spare = NULL;
+ spareSize = 0;
+ } else if (dev->param.inband_tags) {
+
+ if (!data) {
+ localData = 1;
+ data = yaffs_get_temp_buffer(dev);
+ }
+ spare = NULL;
+ spareSize = 0;
+ } else {
+ spare = &pt;
+ spareSize = sizeof(struct yaffs_packed_tags2);
+ }
+
+ retval = geometry->readChunk(dev, nand_chunk,
+ data,
+ data ? dev->param.total_bytes_per_chunk : 0,
+ spare, spareSize,
+ &eccStatus);
+
+ if (dev->param.inband_tags) {
+ if (tags) {
+ struct yaffs_packed_tags2_tags_only *pt2tp;
+ pt2tp = (struct yaffs_packed_tags2_tags_only *)
+ &data[dev->data_bytes_per_chunk];
+ yaffs_unpack_tags2_tags_only(tags, pt2tp);
+ }
+ } else {
+ if (tags)
+ yaffs_unpack_tags2(tags, &pt, !dev->param.no_tags_ecc);
+ }
+
+ if (tags && tags->chunk_used) {
+ if (eccStatus < 0 ||
+ tags->ecc_result == YAFFS_ECC_RESULT_UNFIXED)
+ tags->ecc_result = YAFFS_ECC_RESULT_UNFIXED;
+ else if (eccStatus > 0 ||
+ tags->ecc_result == YAFFS_ECC_RESULT_FIXED)
+ tags->ecc_result = YAFFS_ECC_RESULT_FIXED;
+ else
+ tags->ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
+ }
+
+ if (localData)
+ yaffs_release_temp_buffer(dev, data);
+
+ return retval;
+}
+
+int ynandif_MarkNANDBlockBad(struct yaffs_dev *dev, int blockId)
+{
+ struct ynandif_Geometry *geometry = (struct ynandif_Geometry *)(dev->driver_context);
+
+ return geometry->markBlockBad(dev, blockId);
+}
+
+int ynandif_EraseBlockInNAND(struct yaffs_dev *dev, int blockId)
+{
+ struct ynandif_Geometry *geometry = (struct ynandif_Geometry *)(dev->driver_context);
+
+ return geometry->eraseBlock(dev, blockId);
+
+}
+
+
+static int ynandif_IsBlockOk(struct yaffs_dev *dev, int blockId)
+{
+ struct ynandif_Geometry *geometry = (struct ynandif_Geometry *)(dev->driver_context);
+
+ return geometry->checkBlockOk(dev, blockId);
+}
+
+int ynandif_QueryNANDBlock(struct yaffs_dev *dev, int blockId,
+ enum yaffs_block_state *state, u32 *seq_number)
+{
+ unsigned chunkNo;
+ struct yaffs_ext_tags tags;
+
+ *seq_number = 0;
+
+ chunkNo = blockId * dev->param.chunks_per_block;
+
+ if (!ynandif_IsBlockOk(dev, blockId)) {
+ *state = YAFFS_BLOCK_STATE_DEAD;
+ } else {
+ ynandif_ReadChunkWithTagsFromNAND(dev, chunkNo, NULL, &tags);
+
+ if (!tags.chunk_used) {
+ *state = YAFFS_BLOCK_STATE_EMPTY;
+ } else {
+ *state = YAFFS_BLOCK_STATE_NEEDS_SCAN;
+ *seq_number = tags.seq_number;
+ }
+ }
+
+ return YAFFS_OK;
+}
+
+
+int ynandif_InitialiseNAND(struct yaffs_dev *dev)
+{
+ struct ynandif_Geometry *geometry = (struct ynandif_Geometry *)(dev->driver_context);
+
+ geometry->initialise(dev);
+
+ return YAFFS_OK;
+}
+
+int ynandif_Deinitialise_flash_fn(struct yaffs_dev *dev)
+{
+ struct ynandif_Geometry *geometry = (struct ynandif_Geometry *)(dev->driver_context);
+
+ geometry->deinitialise(dev);
+
+ return YAFFS_OK;
+}
+
+
+struct yaffs_dev *
+ yaffs_add_dev_from_geometry(const YCHAR *name,
+ const struct ynandif_Geometry *geometry)
+{
+ YCHAR *clonedName = malloc(sizeof(YCHAR) *
+ (strnlen(name, YAFFS_MAX_NAME_LENGTH)+1));
+ struct yaffs_dev *dev = malloc(sizeof(struct yaffs_dev));
+ struct yaffs_param *param;
+
+ if (dev && clonedName) {
+ memset(dev, 0, sizeof(struct yaffs_dev));
+ strcpy(clonedName, name);
+
+ param = &dev->param;
+
+ param->name = clonedName;
+ param->write_chunk_tags_fn = ynandif_WriteChunkWithTagsToNAND;
+ param->read_chunk_tags_fn = ynandif_ReadChunkWithTagsFromNAND;
+ param->erase_fn = ynandif_EraseBlockInNAND;
+ param->initialise_flash_fn = ynandif_InitialiseNAND;
+ param->query_block_fn = ynandif_QueryNANDBlock;
+ param->bad_block_fn = ynandif_MarkNANDBlockBad;
+ param->n_caches = 20;
+ param->start_block = geometry->start_block;
+ param->end_block = geometry->end_block;
+ param->total_bytes_per_chunk = geometry->dataSize;
+ param->spare_bytes_per_chunk = geometry->spareSize;
+ param->inband_tags = geometry->inband_tags;
+ param->chunks_per_block = geometry->pagesPerBlock;
+ param->use_nand_ecc = geometry->hasECC;
+ param->is_yaffs2 = geometry->useYaffs2;
+ param->n_reserved_blocks = 5;
+ dev->driver_context = (void *)geometry;
+
+ yaffs_add_device(dev);
+
+ return dev;
+ }
+
+ free(dev);
+ free(clonedName);
+
+ return NULL;
+}
diff --git a/roms/u-boot/fs/yaffs2/yaffs_nandif.h b/roms/u-boot/fs/yaffs2/yaffs_nandif.h
new file mode 100644
index 000000000..e780f7f3b
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_nandif.h
@@ -0,0 +1,65 @@
+/*
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ */
+
+
+#ifndef __YNANDIF_H__
+#define __YNANDIF_H__
+
+#include "yaffs_guts.h"
+
+struct ynandif_Geometry {
+ unsigned start_block;
+ unsigned end_block;
+ unsigned dataSize;
+ unsigned spareSize;
+ unsigned pagesPerBlock;
+ unsigned hasECC;
+ unsigned inband_tags;
+ unsigned useYaffs2;
+
+ int (*initialise)(struct yaffs_dev *dev);
+ int (*deinitialise)(struct yaffs_dev *dev);
+
+ int (*readChunk) (struct yaffs_dev *dev,
+ unsigned pageId,
+ unsigned char *data,
+ unsigned dataLength,
+ unsigned char *spare,
+ unsigned spareLength,
+ int *eccStatus);
+ /* ECC status is set to 0 for OK, 1 for fixed, -1 for unfixed. */
+
+ int (*writeChunk)(struct yaffs_dev *dev,
+ unsigned pageId,
+ const unsigned char *data,
+ unsigned dataLength,
+ const unsigned char *spare,
+ unsigned spareLength);
+
+ int (*eraseBlock)(struct yaffs_dev *dev, unsigned blockId);
+
+ int (*checkBlockOk)(struct yaffs_dev *dev, unsigned blockId);
+ int (*markBlockBad)(struct yaffs_dev *dev, unsigned blockId);
+
+ void *privateData;
+
+};
+
+struct yaffs_dev *
+ yaffs_add_dev_from_geometry(const YCHAR *name,
+ const struct ynandif_Geometry *geometry);
+
+
+#endif
diff --git a/roms/u-boot/fs/yaffs2/yaffs_osglue.h b/roms/u-boot/fs/yaffs2/yaffs_osglue.h
new file mode 100644
index 000000000..f629a4b59
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_osglue.h
@@ -0,0 +1,41 @@
+/*
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ */
+
+/*
+ * Header file for using yaffs in an application via
+ * a direct interface.
+ */
+
+
+#ifndef __YAFFS_OSGLUE_H__
+#define __YAFFS_OSGLUE_H__
+
+
+#include "yportenv.h"
+
+void yaffsfs_Lock(void);
+void yaffsfs_Unlock(void);
+
+u32 yaffsfs_CurrentTime(void);
+
+void yaffsfs_SetError(int err);
+
+void *yaffsfs_malloc(size_t size);
+void yaffsfs_free(void *ptr);
+
+void yaffsfs_OSInitialisation(void);
+
+
+#endif
diff --git a/roms/u-boot/fs/yaffs2/yaffs_packedtags1.c b/roms/u-boot/fs/yaffs2/yaffs_packedtags1.c
new file mode 100644
index 000000000..dd9a331d8
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_packedtags1.c
@@ -0,0 +1,56 @@
+/*
+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "yaffs_packedtags1.h"
+#include "yportenv.h"
+
+static const u8 all_ff[20] = {
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff
+};
+
+void yaffs_pack_tags1(struct yaffs_packed_tags1 *pt,
+ const struct yaffs_ext_tags *t)
+{
+ pt->chunk_id = t->chunk_id;
+ pt->serial_number = t->serial_number;
+ pt->n_bytes = t->n_bytes;
+ pt->obj_id = t->obj_id;
+ pt->ecc = 0;
+ pt->deleted = (t->is_deleted) ? 0 : 1;
+ pt->unused_stuff = 0;
+ pt->should_be_ff = 0xffffffff;
+}
+
+void yaffs_unpack_tags1(struct yaffs_ext_tags *t,
+ const struct yaffs_packed_tags1 *pt)
+{
+
+ if (memcmp(all_ff, pt, sizeof(struct yaffs_packed_tags1))) {
+ t->block_bad = 0;
+ if (pt->should_be_ff != 0xffffffff)
+ t->block_bad = 1;
+ t->chunk_used = 1;
+ t->obj_id = pt->obj_id;
+ t->chunk_id = pt->chunk_id;
+ t->n_bytes = pt->n_bytes;
+ t->ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
+ t->is_deleted = (pt->deleted) ? 0 : 1;
+ t->serial_number = pt->serial_number;
+ } else {
+ memset(t, 0, sizeof(struct yaffs_ext_tags));
+ }
+}
diff --git a/roms/u-boot/fs/yaffs2/yaffs_packedtags1.h b/roms/u-boot/fs/yaffs2/yaffs_packedtags1.h
new file mode 100644
index 000000000..b80f0a5b1
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_packedtags1.h
@@ -0,0 +1,39 @@
+/*
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ */
+
+/* This is used to pack YAFFS1 tags, not YAFFS2 tags. */
+
+#ifndef __YAFFS_PACKEDTAGS1_H__
+#define __YAFFS_PACKEDTAGS1_H__
+
+#include "yaffs_guts.h"
+
+struct yaffs_packed_tags1 {
+ unsigned chunk_id:20;
+ unsigned serial_number:2;
+ unsigned n_bytes:10;
+ unsigned obj_id:18;
+ unsigned ecc:12;
+ unsigned deleted:1;
+ unsigned unused_stuff:1;
+ unsigned should_be_ff;
+
+};
+
+void yaffs_pack_tags1(struct yaffs_packed_tags1 *pt,
+ const struct yaffs_ext_tags *t);
+void yaffs_unpack_tags1(struct yaffs_ext_tags *t,
+ const struct yaffs_packed_tags1 *pt);
+#endif
diff --git a/roms/u-boot/fs/yaffs2/yaffs_packedtags2.c b/roms/u-boot/fs/yaffs2/yaffs_packedtags2.c
new file mode 100644
index 000000000..e1d18cc33
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_packedtags2.c
@@ -0,0 +1,197 @@
+/*
+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "yaffs_packedtags2.h"
+#include "yportenv.h"
+#include "yaffs_trace.h"
+
+/* This code packs a set of extended tags into a binary structure for
+ * NAND storage
+ */
+
+/* Some of the information is "extra" struff which can be packed in to
+ * speed scanning
+ * This is defined by having the EXTRA_HEADER_INFO_FLAG set.
+ */
+
+/* Extra flags applied to chunk_id */
+
+#define EXTRA_HEADER_INFO_FLAG 0x80000000
+#define EXTRA_SHRINK_FLAG 0x40000000
+#define EXTRA_SHADOWS_FLAG 0x20000000
+#define EXTRA_SPARE_FLAGS 0x10000000
+
+#define ALL_EXTRA_FLAGS 0xf0000000
+
+/* Also, the top 4 bits of the object Id are set to the object type. */
+#define EXTRA_OBJECT_TYPE_SHIFT (28)
+#define EXTRA_OBJECT_TYPE_MASK ((0x0f) << EXTRA_OBJECT_TYPE_SHIFT)
+
+static void yaffs_dump_packed_tags2_tags_only(
+ const struct yaffs_packed_tags2_tags_only *ptt)
+{
+ yaffs_trace(YAFFS_TRACE_MTD,
+ "packed tags obj %d chunk %d byte %d seq %d",
+ ptt->obj_id, ptt->chunk_id, ptt->n_bytes, ptt->seq_number);
+}
+
+static void yaffs_dump_packed_tags2(const struct yaffs_packed_tags2 *pt)
+{
+ yaffs_dump_packed_tags2_tags_only(&pt->t);
+}
+
+static void yaffs_dump_tags2(const struct yaffs_ext_tags *t)
+{
+ yaffs_trace(YAFFS_TRACE_MTD,
+ "ext.tags eccres %d blkbad %d chused %d obj %d chunk%d byte %d del %d ser %d seq %d",
+ t->ecc_result, t->block_bad, t->chunk_used, t->obj_id,
+ t->chunk_id, t->n_bytes, t->is_deleted, t->serial_number,
+ t->seq_number);
+
+}
+
+static int yaffs_check_tags_extra_packable(const struct yaffs_ext_tags *t)
+{
+ if (t->chunk_id != 0 || !t->extra_available)
+ return 0;
+
+ /* Check if the file size is too long to store */
+ if (t->extra_obj_type == YAFFS_OBJECT_TYPE_FILE &&
+ (t->extra_file_size >> 31) != 0)
+ return 0;
+ return 1;
+}
+
+void yaffs_pack_tags2_tags_only(struct yaffs_packed_tags2_tags_only *ptt,
+ const struct yaffs_ext_tags *t)
+{
+ ptt->chunk_id = t->chunk_id;
+ ptt->seq_number = t->seq_number;
+ ptt->n_bytes = t->n_bytes;
+ ptt->obj_id = t->obj_id;
+
+ /* Only store extra tags for object headers.
+ * If it is a file then only store if the file size is short\
+ * enough to fit.
+ */
+ if (yaffs_check_tags_extra_packable(t)) {
+ /* Store the extra header info instead */
+ /* We save the parent object in the chunk_id */
+ ptt->chunk_id = EXTRA_HEADER_INFO_FLAG | t->extra_parent_id;
+ if (t->extra_is_shrink)
+ ptt->chunk_id |= EXTRA_SHRINK_FLAG;
+ if (t->extra_shadows)
+ ptt->chunk_id |= EXTRA_SHADOWS_FLAG;
+
+ ptt->obj_id &= ~EXTRA_OBJECT_TYPE_MASK;
+ ptt->obj_id |= (t->extra_obj_type << EXTRA_OBJECT_TYPE_SHIFT);
+
+ if (t->extra_obj_type == YAFFS_OBJECT_TYPE_HARDLINK)
+ ptt->n_bytes = t->extra_equiv_id;
+ else if (t->extra_obj_type == YAFFS_OBJECT_TYPE_FILE)
+ ptt->n_bytes = (unsigned) t->extra_file_size;
+ else
+ ptt->n_bytes = 0;
+ }
+
+ yaffs_dump_packed_tags2_tags_only(ptt);
+ yaffs_dump_tags2(t);
+}
+
+void yaffs_pack_tags2(struct yaffs_packed_tags2 *pt,
+ const struct yaffs_ext_tags *t, int tags_ecc)
+{
+ yaffs_pack_tags2_tags_only(&pt->t, t);
+
+ if (tags_ecc)
+ yaffs_ecc_calc_other((unsigned char *)&pt->t,
+ sizeof(struct yaffs_packed_tags2_tags_only),
+ &pt->ecc);
+}
+
+void yaffs_unpack_tags2_tags_only(struct yaffs_ext_tags *t,
+ struct yaffs_packed_tags2_tags_only *ptt)
+{
+ memset(t, 0, sizeof(struct yaffs_ext_tags));
+
+ if (ptt->seq_number == 0xffffffff)
+ return;
+
+ t->block_bad = 0;
+ t->chunk_used = 1;
+ t->obj_id = ptt->obj_id;
+ t->chunk_id = ptt->chunk_id;
+ t->n_bytes = ptt->n_bytes;
+ t->is_deleted = 0;
+ t->serial_number = 0;
+ t->seq_number = ptt->seq_number;
+
+ /* Do extra header info stuff */
+ if (ptt->chunk_id & EXTRA_HEADER_INFO_FLAG) {
+ t->chunk_id = 0;
+ t->n_bytes = 0;
+
+ t->extra_available = 1;
+ t->extra_parent_id = ptt->chunk_id & (~(ALL_EXTRA_FLAGS));
+ t->extra_is_shrink = ptt->chunk_id & EXTRA_SHRINK_FLAG ? 1 : 0;
+ t->extra_shadows = ptt->chunk_id & EXTRA_SHADOWS_FLAG ? 1 : 0;
+ t->extra_obj_type = ptt->obj_id >> EXTRA_OBJECT_TYPE_SHIFT;
+ t->obj_id &= ~EXTRA_OBJECT_TYPE_MASK;
+
+ if (t->extra_obj_type == YAFFS_OBJECT_TYPE_HARDLINK)
+ t->extra_equiv_id = ptt->n_bytes;
+ else
+ t->extra_file_size = ptt->n_bytes;
+ }
+ yaffs_dump_packed_tags2_tags_only(ptt);
+ yaffs_dump_tags2(t);
+}
+
+void yaffs_unpack_tags2(struct yaffs_ext_tags *t, struct yaffs_packed_tags2 *pt,
+ int tags_ecc)
+{
+ enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
+
+ if (pt->t.seq_number != 0xffffffff && tags_ecc) {
+ /* Chunk is in use and we need to do ECC */
+
+ struct yaffs_ecc_other ecc;
+ int result;
+ yaffs_ecc_calc_other((unsigned char *)&pt->t,
+ sizeof(struct yaffs_packed_tags2_tags_only),
+ &ecc);
+ result =
+ yaffs_ecc_correct_other((unsigned char *)&pt->t,
+ sizeof(struct yaffs_packed_tags2_tags_only),
+ &pt->ecc, &ecc);
+ switch (result) {
+ case 0:
+ ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
+ break;
+ case 1:
+ ecc_result = YAFFS_ECC_RESULT_FIXED;
+ break;
+ case -1:
+ ecc_result = YAFFS_ECC_RESULT_UNFIXED;
+ break;
+ default:
+ ecc_result = YAFFS_ECC_RESULT_UNKNOWN;
+ }
+ }
+ yaffs_unpack_tags2_tags_only(t, &pt->t);
+
+ t->ecc_result = ecc_result;
+
+ yaffs_dump_packed_tags2(pt);
+ yaffs_dump_tags2(t);
+}
diff --git a/roms/u-boot/fs/yaffs2/yaffs_packedtags2.h b/roms/u-boot/fs/yaffs2/yaffs_packedtags2.h
new file mode 100644
index 000000000..675e71946
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_packedtags2.h
@@ -0,0 +1,47 @@
+/*
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ */
+
+/* This is used to pack YAFFS2 tags, not YAFFS1tags. */
+
+#ifndef __YAFFS_PACKEDTAGS2_H__
+#define __YAFFS_PACKEDTAGS2_H__
+
+#include "yaffs_guts.h"
+#include "yaffs_ecc.h"
+
+struct yaffs_packed_tags2_tags_only {
+ unsigned seq_number;
+ unsigned obj_id;
+ unsigned chunk_id;
+ unsigned n_bytes;
+};
+
+struct yaffs_packed_tags2 {
+ struct yaffs_packed_tags2_tags_only t;
+ struct yaffs_ecc_other ecc;
+};
+
+/* Full packed tags with ECC, used for oob tags */
+void yaffs_pack_tags2(struct yaffs_packed_tags2 *pt,
+ const struct yaffs_ext_tags *t, int tags_ecc);
+void yaffs_unpack_tags2(struct yaffs_ext_tags *t, struct yaffs_packed_tags2 *pt,
+ int tags_ecc);
+
+/* Only the tags part (no ECC for use with inband tags */
+void yaffs_pack_tags2_tags_only(struct yaffs_packed_tags2_tags_only *pt,
+ const struct yaffs_ext_tags *t);
+void yaffs_unpack_tags2_tags_only(struct yaffs_ext_tags *t,
+ struct yaffs_packed_tags2_tags_only *pt);
+#endif
diff --git a/roms/u-boot/fs/yaffs2/yaffs_qsort.c b/roms/u-boot/fs/yaffs2/yaffs_qsort.c
new file mode 100644
index 000000000..32c767f35
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_qsort.c
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ */
+
+#include "yportenv.h"
+#include <sort.h>
+/* #include <linux/string.h> */
+
+/*
+ * Qsort routine from Bentley & McIlroy's "Engineering a Sort Function".
+ */
+#define swapcode(TYPE, parmi, parmj, n) do { \
+ long i = (n) / sizeof(TYPE); \
+ register TYPE *pi = (TYPE *) (parmi); \
+ register TYPE *pj = (TYPE *) (parmj); \
+ do { \
+ register TYPE t = *pi; \
+ *pi++ = *pj; \
+ *pj++ = t; \
+ } while (--i > 0); \
+} while (0)
+
+#define SWAPINIT(a, es) swaptype = ((char *)a - (char *)0) % sizeof(long) || \
+ es % sizeof(long) ? 2 : es == sizeof(long) ? 0 : 1;
+
+static inline void
+swapfunc(char *a, char *b, int n, int swaptype)
+{
+ if (swaptype <= 1)
+ swapcode(long, a, b, n);
+ else
+ swapcode(char, a, b, n);
+}
+
+#define yswap(a, b) do { \
+ if (swaptype == 0) { \
+ long t = *(long *)(a); \
+ *(long *)(a) = *(long *)(b); \
+ *(long *)(b) = t; \
+ } else \
+ swapfunc(a, b, es, swaptype); \
+} while (0)
+
+#define vecswap(a, b, n) if ((n) > 0) swapfunc(a, b, n, swaptype)
+
+static inline char *
+med3(char *a, char *b, char *c, int (*cmp)(const void *, const void *))
+{
+ return cmp(a, b) < 0 ?
+ (cmp(b, c) < 0 ? b : (cmp(a, c) < 0 ? c : a))
+ : (cmp(b, c) > 0 ? b : (cmp(a, c) < 0 ? a : c));
+}
+
+#ifndef min
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+
+void
+yaffs_qsort(void *aa, size_t n, size_t es,
+ int (*cmp)(const void *, const void *))
+{
+ char *pa, *pb, *pc, *pd, *pl, *pm, *pn;
+ int d, r, swaptype, swap_cnt;
+ register char *a = aa;
+
+loop: SWAPINIT(a, es);
+ swap_cnt = 0;
+ if (n < 7) {
+ for (pm = (char *)a + es; pm < (char *) a + n * es; pm += es)
+ for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0;
+ pl -= es)
+ yswap(pl, pl - es);
+ return;
+ }
+ pm = (char *)a + (n / 2) * es;
+ if (n > 7) {
+ pl = (char *)a;
+ pn = (char *)a + (n - 1) * es;
+ if (n > 40) {
+ d = (n / 8) * es;
+ pl = med3(pl, pl + d, pl + 2 * d, cmp);
+ pm = med3(pm - d, pm, pm + d, cmp);
+ pn = med3(pn - 2 * d, pn - d, pn, cmp);
+ }
+ pm = med3(pl, pm, pn, cmp);
+ }
+ yswap(a, pm);
+ pa = pb = (char *)a + es;
+
+ pc = pd = (char *)a + (n - 1) * es;
+ for (;;) {
+ while (pb <= pc && (r = cmp(pb, a)) <= 0) {
+ if (r == 0) {
+ swap_cnt = 1;
+ yswap(pa, pb);
+ pa += es;
+ }
+ pb += es;
+ }
+ while (pb <= pc && (r = cmp(pc, a)) >= 0) {
+ if (r == 0) {
+ swap_cnt = 1;
+ yswap(pc, pd);
+ pd -= es;
+ }
+ pc -= es;
+ }
+ if (pb > pc)
+ break;
+ yswap(pb, pc);
+ swap_cnt = 1;
+ pb += es;
+ pc -= es;
+ }
+ if (swap_cnt == 0) { /* Switch to insertion sort */
+ for (pm = (char *) a + es; pm < (char *) a + n * es; pm += es)
+ for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0;
+ pl -= es)
+ yswap(pl, pl - es);
+ return;
+ }
+
+ pn = (char *)a + n * es;
+ r = min(pa - (char *)a, pb - pa);
+ vecswap(a, pb - r, r);
+ r = min((long)(pd - pc), (long)(pn - pd - es));
+ vecswap(pb, pn - r, r);
+ r = pb - pa;
+ if (r > es)
+ yaffs_qsort(a, r / es, es, cmp);
+ r = pd - pc;
+ if (r > es) {
+ /* Iterate rather than recurse to save stack space */
+ a = pn - r;
+ n = r / es;
+ goto loop;
+ }
+/* yaffs_qsort(pn - r, r / es, es, cmp);*/
+}
diff --git a/roms/u-boot/fs/yaffs2/yaffs_summary.c b/roms/u-boot/fs/yaffs2/yaffs_summary.c
new file mode 100644
index 000000000..4f9449a84
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_summary.c
@@ -0,0 +1,310 @@
+/*
+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/* Summaries write the useful part of the tags for the chunks in a block into an
+ * an array which is written to the last n chunks of the block.
+ * Reading the summaries gives all the tags for the block in one read. Much
+ * faster.
+ *
+ * Chunks holding summaries are marked with tags making it look like
+ * they are part of a fake file.
+ *
+ * The summary could also be used during gc.
+ *
+ */
+
+#include "yaffs_summary.h"
+#include "yaffs_packedtags2.h"
+#include "yaffs_nand.h"
+#include "yaffs_getblockinfo.h"
+#include "yaffs_bitmap.h"
+#include <dm/devres.h>
+
+/*
+ * The summary is built up in an array of summary tags.
+ * This gets written to the last one or two (maybe more) chunks in a block.
+ * A summary header is written as the first part of each chunk of summary data.
+ * The summary header must match or the summary is rejected.
+ */
+
+/* Summary tags don't need the sequence number because that is redundant. */
+struct yaffs_summary_tags {
+ unsigned obj_id;
+ unsigned chunk_id;
+ unsigned n_bytes;
+};
+
+/* Summary header */
+struct yaffs_summary_header {
+ unsigned version; /* Must match current version */
+ unsigned block; /* Must be this block */
+ unsigned seq; /* Must be this sequence number */
+ unsigned sum; /* Just add up all the bytes in the tags */
+};
+
+
+static void yaffs_summary_clear(struct yaffs_dev *dev)
+{
+ if (!dev->sum_tags)
+ return;
+ memset(dev->sum_tags, 0, dev->chunks_per_summary *
+ sizeof(struct yaffs_summary_tags));
+}
+
+
+void yaffs_summary_deinit(struct yaffs_dev *dev)
+{
+ kfree(dev->sum_tags);
+ dev->sum_tags = NULL;
+ kfree(dev->gc_sum_tags);
+ dev->gc_sum_tags = NULL;
+ dev->chunks_per_summary = 0;
+}
+
+int yaffs_summary_init(struct yaffs_dev *dev)
+{
+ int sum_bytes;
+ int chunks_used; /* Number of chunks used by summary */
+ int sum_tags_bytes;
+
+ sum_bytes = dev->param.chunks_per_block *
+ sizeof(struct yaffs_summary_tags);
+
+ chunks_used = (sum_bytes + dev->data_bytes_per_chunk - 1)/
+ (dev->data_bytes_per_chunk -
+ sizeof(struct yaffs_summary_header));
+
+ dev->chunks_per_summary = dev->param.chunks_per_block - chunks_used;
+ sum_tags_bytes = sizeof(struct yaffs_summary_tags) *
+ dev->chunks_per_summary;
+ dev->sum_tags = kmalloc(sum_tags_bytes, GFP_NOFS);
+ dev->gc_sum_tags = kmalloc(sum_tags_bytes, GFP_NOFS);
+ if (!dev->sum_tags || !dev->gc_sum_tags) {
+ yaffs_summary_deinit(dev);
+ return YAFFS_FAIL;
+ }
+
+ yaffs_summary_clear(dev);
+
+ return YAFFS_OK;
+}
+
+static unsigned yaffs_summary_sum(struct yaffs_dev *dev)
+{
+ u8 *sum_buffer = (u8 *)dev->sum_tags;
+ int i;
+ unsigned sum = 0;
+
+ i = sizeof(struct yaffs_summary_tags) *
+ dev->chunks_per_summary;
+ while (i > 0) {
+ sum += *sum_buffer;
+ sum_buffer++;
+ i--;
+ }
+
+ return sum;
+}
+
+static int yaffs_summary_write(struct yaffs_dev *dev, int blk)
+{
+ struct yaffs_ext_tags tags;
+ u8 *buffer;
+ u8 *sum_buffer = (u8 *)dev->sum_tags;
+ int n_bytes;
+ int chunk_in_nand;
+ int chunk_in_block;
+ int result;
+ int this_tx;
+ struct yaffs_summary_header hdr;
+ int sum_bytes_per_chunk = dev->data_bytes_per_chunk - sizeof(hdr);
+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk);
+
+ buffer = yaffs_get_temp_buffer(dev);
+ n_bytes = sizeof(struct yaffs_summary_tags) *
+ dev->chunks_per_summary;
+ memset(&tags, 0, sizeof(struct yaffs_ext_tags));
+ tags.obj_id = YAFFS_OBJECTID_SUMMARY;
+ tags.chunk_id = 1;
+ chunk_in_block = dev->chunks_per_summary;
+ chunk_in_nand = dev->alloc_block * dev->param.chunks_per_block +
+ dev->chunks_per_summary;
+ hdr.version = YAFFS_SUMMARY_VERSION;
+ hdr.block = blk;
+ hdr.seq = bi->seq_number;
+ hdr.sum = yaffs_summary_sum(dev);
+
+ do {
+ this_tx = n_bytes;
+ if (this_tx > sum_bytes_per_chunk)
+ this_tx = sum_bytes_per_chunk;
+ memcpy(buffer, &hdr, sizeof(hdr));
+ memcpy(buffer + sizeof(hdr), sum_buffer, this_tx);
+ tags.n_bytes = this_tx + sizeof(hdr);
+ result = yaffs_wr_chunk_tags_nand(dev, chunk_in_nand,
+ buffer, &tags);
+
+ if (result != YAFFS_OK)
+ break;
+ yaffs_set_chunk_bit(dev, blk, chunk_in_block);
+ bi->pages_in_use++;
+ dev->n_free_chunks--;
+
+ n_bytes -= this_tx;
+ sum_buffer += this_tx;
+ chunk_in_nand++;
+ chunk_in_block++;
+ tags.chunk_id++;
+ } while (result == YAFFS_OK && n_bytes > 0);
+ yaffs_release_temp_buffer(dev, buffer);
+
+
+ if (result == YAFFS_OK)
+ bi->has_summary = 1;
+
+
+ return result;
+}
+
+int yaffs_summary_read(struct yaffs_dev *dev,
+ struct yaffs_summary_tags *st,
+ int blk)
+{
+ struct yaffs_ext_tags tags;
+ u8 *buffer;
+ u8 *sum_buffer = (u8 *)st;
+ int n_bytes;
+ int chunk_id;
+ int chunk_in_nand;
+ int chunk_in_block;
+ int result;
+ int this_tx;
+ struct yaffs_summary_header hdr;
+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk);
+ int sum_bytes_per_chunk = dev->data_bytes_per_chunk - sizeof(hdr);
+
+ buffer = yaffs_get_temp_buffer(dev);
+ n_bytes = sizeof(struct yaffs_summary_tags) * dev->chunks_per_summary;
+ chunk_in_block = dev->chunks_per_summary;
+ chunk_in_nand = blk * dev->param.chunks_per_block +
+ dev->chunks_per_summary;
+ chunk_id = 1;
+ do {
+ this_tx = n_bytes;
+ if (this_tx > sum_bytes_per_chunk)
+ this_tx = sum_bytes_per_chunk;
+ result = yaffs_rd_chunk_tags_nand(dev, chunk_in_nand,
+ buffer, &tags);
+
+ if (tags.chunk_id != chunk_id ||
+ tags.obj_id != YAFFS_OBJECTID_SUMMARY ||
+ tags.chunk_used == 0 ||
+ tags.ecc_result > YAFFS_ECC_RESULT_FIXED ||
+ tags.n_bytes != (this_tx + sizeof(hdr)))
+ result = YAFFS_FAIL;
+ if (result != YAFFS_OK)
+ break;
+
+ if (st == dev->sum_tags) {
+ /* If we're scanning then update the block info */
+ yaffs_set_chunk_bit(dev, blk, chunk_in_block);
+ bi->pages_in_use++;
+ }
+ memcpy(&hdr, buffer, sizeof(hdr));
+ memcpy(sum_buffer, buffer + sizeof(hdr), this_tx);
+ n_bytes -= this_tx;
+ sum_buffer += this_tx;
+ chunk_in_nand++;
+ chunk_in_block++;
+ chunk_id++;
+ } while (result == YAFFS_OK && n_bytes > 0);
+ yaffs_release_temp_buffer(dev, buffer);
+
+ if (result == YAFFS_OK) {
+ /* Verify header */
+ if (hdr.version != YAFFS_SUMMARY_VERSION ||
+ hdr.seq != bi->seq_number ||
+ hdr.sum != yaffs_summary_sum(dev))
+ result = YAFFS_FAIL;
+ }
+
+ if (st == dev->sum_tags && result == YAFFS_OK)
+ bi->has_summary = 1;
+
+ return result;
+}
+
+int yaffs_summary_add(struct yaffs_dev *dev,
+ struct yaffs_ext_tags *tags,
+ int chunk_in_nand)
+{
+ struct yaffs_packed_tags2_tags_only tags_only;
+ struct yaffs_summary_tags *sum_tags;
+ int block_in_nand = chunk_in_nand / dev->param.chunks_per_block;
+ int chunk_in_block = chunk_in_nand % dev->param.chunks_per_block;
+
+ if (!dev->sum_tags)
+ return YAFFS_OK;
+
+ if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) {
+ yaffs_pack_tags2_tags_only(&tags_only, tags);
+ sum_tags = &dev->sum_tags[chunk_in_block];
+ sum_tags->chunk_id = tags_only.chunk_id;
+ sum_tags->n_bytes = tags_only.n_bytes;
+ sum_tags->obj_id = tags_only.obj_id;
+
+ if (chunk_in_block == dev->chunks_per_summary - 1) {
+ /* Time to write out the summary */
+ yaffs_summary_write(dev, block_in_nand);
+ yaffs_summary_clear(dev);
+ yaffs_skip_rest_of_block(dev);
+ }
+ }
+ return YAFFS_OK;
+}
+
+int yaffs_summary_fetch(struct yaffs_dev *dev,
+ struct yaffs_ext_tags *tags,
+ int chunk_in_block)
+{
+ struct yaffs_packed_tags2_tags_only tags_only;
+ struct yaffs_summary_tags *sum_tags;
+ if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) {
+ sum_tags = &dev->sum_tags[chunk_in_block];
+ tags_only.chunk_id = sum_tags->chunk_id;
+ tags_only.n_bytes = sum_tags->n_bytes;
+ tags_only.obj_id = sum_tags->obj_id;
+ yaffs_unpack_tags2_tags_only(tags, &tags_only);
+ return YAFFS_OK;
+ }
+ return YAFFS_FAIL;
+}
+
+void yaffs_summary_gc(struct yaffs_dev *dev, int blk)
+{
+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk);
+ int i;
+
+ if (!bi->has_summary)
+ return;
+
+ for (i = dev->chunks_per_summary;
+ i < dev->param.chunks_per_block;
+ i++) {
+ if (yaffs_check_chunk_bit(dev, blk, i)) {
+ yaffs_clear_chunk_bit(dev, blk, i);
+ bi->pages_in_use--;
+ dev->n_free_chunks++;
+ }
+ }
+}
diff --git a/roms/u-boot/fs/yaffs2/yaffs_summary.h b/roms/u-boot/fs/yaffs2/yaffs_summary.h
new file mode 100644
index 000000000..be141d073
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_summary.h
@@ -0,0 +1,37 @@
+/*
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ */
+
+#ifndef __YAFFS_SUMMARY_H__
+#define __YAFFS_SUMMARY_H__
+
+#include "yaffs_packedtags2.h"
+
+
+int yaffs_summary_init(struct yaffs_dev *dev);
+void yaffs_summary_deinit(struct yaffs_dev *dev);
+
+int yaffs_summary_add(struct yaffs_dev *dev,
+ struct yaffs_ext_tags *tags,
+ int chunk_in_block);
+int yaffs_summary_fetch(struct yaffs_dev *dev,
+ struct yaffs_ext_tags *tags,
+ int chunk_in_block);
+int yaffs_summary_read(struct yaffs_dev *dev,
+ struct yaffs_summary_tags *st,
+ int blk);
+void yaffs_summary_gc(struct yaffs_dev *dev, int blk);
+
+
+#endif
diff --git a/roms/u-boot/fs/yaffs2/yaffs_tagscompat.c b/roms/u-boot/fs/yaffs2/yaffs_tagscompat.c
new file mode 100644
index 000000000..9ac5896da
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_tagscompat.c
@@ -0,0 +1,407 @@
+/*
+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "yaffs_guts.h"
+#include "yaffs_tagscompat.h"
+#include "yaffs_ecc.h"
+#include "yaffs_getblockinfo.h"
+#include "yaffs_trace.h"
+
+static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk);
+
+
+/********** Tags ECC calculations *********/
+
+void yaffs_calc_ecc(const u8 *data, struct yaffs_spare *spare)
+{
+ yaffs_ecc_calc(data, spare->ecc1);
+ yaffs_ecc_calc(&data[256], spare->ecc2);
+}
+
+void yaffs_calc_tags_ecc(struct yaffs_tags *tags)
+{
+ /* Calculate an ecc */
+ unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes;
+ unsigned i, j;
+ unsigned ecc = 0;
+ unsigned bit = 0;
+
+ tags->ecc = 0;
+
+ for (i = 0; i < 8; i++) {
+ for (j = 1; j & 0xff; j <<= 1) {
+ bit++;
+ if (b[i] & j)
+ ecc ^= bit;
+ }
+ }
+ tags->ecc = ecc;
+}
+
+int yaffs_check_tags_ecc(struct yaffs_tags *tags)
+{
+ unsigned ecc = tags->ecc;
+
+ yaffs_calc_tags_ecc(tags);
+
+ ecc ^= tags->ecc;
+
+ if (ecc && ecc <= 64) {
+ /* TODO: Handle the failure better. Retire? */
+ unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes;
+
+ ecc--;
+
+ b[ecc / 8] ^= (1 << (ecc & 7));
+
+ /* Now recvalc the ecc */
+ yaffs_calc_tags_ecc(tags);
+
+ return 1; /* recovered error */
+ } else if (ecc) {
+ /* Wierd ecc failure value */
+ /* TODO Need to do somethiong here */
+ return -1; /* unrecovered error */
+ }
+ return 0;
+}
+
+/********** Tags **********/
+
+static void yaffs_load_tags_to_spare(struct yaffs_spare *spare_ptr,
+ struct yaffs_tags *tags_ptr)
+{
+ union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr;
+
+ yaffs_calc_tags_ecc(tags_ptr);
+
+ spare_ptr->tb0 = tu->as_bytes[0];
+ spare_ptr->tb1 = tu->as_bytes[1];
+ spare_ptr->tb2 = tu->as_bytes[2];
+ spare_ptr->tb3 = tu->as_bytes[3];
+ spare_ptr->tb4 = tu->as_bytes[4];
+ spare_ptr->tb5 = tu->as_bytes[5];
+ spare_ptr->tb6 = tu->as_bytes[6];
+ spare_ptr->tb7 = tu->as_bytes[7];
+}
+
+static void yaffs_get_tags_from_spare(struct yaffs_dev *dev,
+ struct yaffs_spare *spare_ptr,
+ struct yaffs_tags *tags_ptr)
+{
+ union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr;
+ int result;
+
+ tu->as_bytes[0] = spare_ptr->tb0;
+ tu->as_bytes[1] = spare_ptr->tb1;
+ tu->as_bytes[2] = spare_ptr->tb2;
+ tu->as_bytes[3] = spare_ptr->tb3;
+ tu->as_bytes[4] = spare_ptr->tb4;
+ tu->as_bytes[5] = spare_ptr->tb5;
+ tu->as_bytes[6] = spare_ptr->tb6;
+ tu->as_bytes[7] = spare_ptr->tb7;
+
+ result = yaffs_check_tags_ecc(tags_ptr);
+ if (result > 0)
+ dev->n_tags_ecc_fixed++;
+ else if (result < 0)
+ dev->n_tags_ecc_unfixed++;
+}
+
+static void yaffs_spare_init(struct yaffs_spare *spare)
+{
+ memset(spare, 0xff, sizeof(struct yaffs_spare));
+}
+
+static int yaffs_wr_nand(struct yaffs_dev *dev,
+ int nand_chunk, const u8 *data,
+ struct yaffs_spare *spare)
+{
+ if (nand_chunk < dev->param.start_block * dev->param.chunks_per_block) {
+ yaffs_trace(YAFFS_TRACE_ERROR,
+ "**>> yaffs chunk %d is not valid",
+ nand_chunk);
+ return YAFFS_FAIL;
+ }
+
+ return dev->param.write_chunk_fn(dev, nand_chunk, data, spare);
+}
+
+static int yaffs_rd_chunk_nand(struct yaffs_dev *dev,
+ int nand_chunk,
+ u8 *data,
+ struct yaffs_spare *spare,
+ enum yaffs_ecc_result *ecc_result,
+ int correct_errors)
+{
+ int ret_val;
+ struct yaffs_spare local_spare;
+
+ if (!spare) {
+ /* If we don't have a real spare, then we use a local one. */
+ /* Need this for the calculation of the ecc */
+ spare = &local_spare;
+ }
+
+ if (!dev->param.use_nand_ecc) {
+ ret_val =
+ dev->param.read_chunk_fn(dev, nand_chunk, data, spare);
+ if (data && correct_errors) {
+ /* Do ECC correction */
+ /* Todo handle any errors */
+ int ecc_result1, ecc_result2;
+ u8 calc_ecc[3];
+
+ yaffs_ecc_calc(data, calc_ecc);
+ ecc_result1 =
+ yaffs_ecc_correct(data, spare->ecc1, calc_ecc);
+ yaffs_ecc_calc(&data[256], calc_ecc);
+ ecc_result2 =
+ yaffs_ecc_correct(&data[256], spare->ecc2,
+ calc_ecc);
+
+ if (ecc_result1 > 0) {
+ yaffs_trace(YAFFS_TRACE_ERROR,
+ "**>>yaffs ecc error fix performed on chunk %d:0",
+ nand_chunk);
+ dev->n_ecc_fixed++;
+ } else if (ecc_result1 < 0) {
+ yaffs_trace(YAFFS_TRACE_ERROR,
+ "**>>yaffs ecc error unfixed on chunk %d:0",
+ nand_chunk);
+ dev->n_ecc_unfixed++;
+ }
+
+ if (ecc_result2 > 0) {
+ yaffs_trace(YAFFS_TRACE_ERROR,
+ "**>>yaffs ecc error fix performed on chunk %d:1",
+ nand_chunk);
+ dev->n_ecc_fixed++;
+ } else if (ecc_result2 < 0) {
+ yaffs_trace(YAFFS_TRACE_ERROR,
+ "**>>yaffs ecc error unfixed on chunk %d:1",
+ nand_chunk);
+ dev->n_ecc_unfixed++;
+ }
+
+ if (ecc_result1 || ecc_result2) {
+ /* We had a data problem on this page */
+ yaffs_handle_rd_data_error(dev, nand_chunk);
+ }
+
+ if (ecc_result1 < 0 || ecc_result2 < 0)
+ *ecc_result = YAFFS_ECC_RESULT_UNFIXED;
+ else if (ecc_result1 > 0 || ecc_result2 > 0)
+ *ecc_result = YAFFS_ECC_RESULT_FIXED;
+ else
+ *ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
+ }
+ } else {
+ /* Must allocate enough memory for spare+2*sizeof(int) */
+ /* for ecc results from device. */
+ struct yaffs_nand_spare nspare;
+
+ memset(&nspare, 0, sizeof(nspare));
+
+ ret_val = dev->param.read_chunk_fn(dev, nand_chunk, data,
+ (struct yaffs_spare *)
+ &nspare);
+ memcpy(spare, &nspare, sizeof(struct yaffs_spare));
+ if (data && correct_errors) {
+ if (nspare.eccres1 > 0) {
+ yaffs_trace(YAFFS_TRACE_ERROR,
+ "**>>mtd ecc error fix performed on chunk %d:0",
+ nand_chunk);
+ } else if (nspare.eccres1 < 0) {
+ yaffs_trace(YAFFS_TRACE_ERROR,
+ "**>>mtd ecc error unfixed on chunk %d:0",
+ nand_chunk);
+ }
+
+ if (nspare.eccres2 > 0) {
+ yaffs_trace(YAFFS_TRACE_ERROR,
+ "**>>mtd ecc error fix performed on chunk %d:1",
+ nand_chunk);
+ } else if (nspare.eccres2 < 0) {
+ yaffs_trace(YAFFS_TRACE_ERROR,
+ "**>>mtd ecc error unfixed on chunk %d:1",
+ nand_chunk);
+ }
+
+ if (nspare.eccres1 || nspare.eccres2) {
+ /* We had a data problem on this page */
+ yaffs_handle_rd_data_error(dev, nand_chunk);
+ }
+
+ if (nspare.eccres1 < 0 || nspare.eccres2 < 0)
+ *ecc_result = YAFFS_ECC_RESULT_UNFIXED;
+ else if (nspare.eccres1 > 0 || nspare.eccres2 > 0)
+ *ecc_result = YAFFS_ECC_RESULT_FIXED;
+ else
+ *ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
+
+ }
+ }
+ return ret_val;
+}
+
+/*
+ * Functions for robustisizing
+ */
+
+static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk)
+{
+ int flash_block = nand_chunk / dev->param.chunks_per_block;
+
+ /* Mark the block for retirement */
+ yaffs_get_block_info(dev, flash_block + dev->block_offset)->
+ needs_retiring = 1;
+ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,
+ "**>>Block %d marked for retirement",
+ flash_block);
+
+ /* TODO:
+ * Just do a garbage collection on the affected block
+ * then retire the block
+ * NB recursion
+ */
+}
+
+int yaffs_tags_compat_wr(struct yaffs_dev *dev,
+ int nand_chunk,
+ const u8 *data, const struct yaffs_ext_tags *ext_tags)
+{
+ struct yaffs_spare spare;
+ struct yaffs_tags tags;
+
+ yaffs_spare_init(&spare);
+
+ if (ext_tags->is_deleted)
+ spare.page_status = 0;
+ else {
+ tags.obj_id = ext_tags->obj_id;
+ tags.chunk_id = ext_tags->chunk_id;
+
+ tags.n_bytes_lsb = ext_tags->n_bytes & (1024 - 1);
+
+ if (dev->data_bytes_per_chunk >= 1024)
+ tags.n_bytes_msb = (ext_tags->n_bytes >> 10) & 3;
+ else
+ tags.n_bytes_msb = 3;
+
+ tags.serial_number = ext_tags->serial_number;
+
+ if (!dev->param.use_nand_ecc && data)
+ yaffs_calc_ecc(data, &spare);
+
+ yaffs_load_tags_to_spare(&spare, &tags);
+ }
+ return yaffs_wr_nand(dev, nand_chunk, data, &spare);
+}
+
+int yaffs_tags_compat_rd(struct yaffs_dev *dev,
+ int nand_chunk,
+ u8 *data, struct yaffs_ext_tags *ext_tags)
+{
+ struct yaffs_spare spare;
+ struct yaffs_tags tags;
+ enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_UNKNOWN;
+ static struct yaffs_spare spare_ff;
+ static int init;
+ int deleted;
+
+ if (!init) {
+ memset(&spare_ff, 0xff, sizeof(spare_ff));
+ init = 1;
+ }
+
+ if (!yaffs_rd_chunk_nand(dev, nand_chunk,
+ data, &spare, &ecc_result, 1))
+ return YAFFS_FAIL;
+
+ /* ext_tags may be NULL */
+ if (!ext_tags)
+ return YAFFS_OK;
+
+ deleted = (hweight8(spare.page_status) < 7) ? 1 : 0;
+
+ ext_tags->is_deleted = deleted;
+ ext_tags->ecc_result = ecc_result;
+ ext_tags->block_bad = 0; /* We're reading it */
+ /* therefore it is not a bad block */
+ ext_tags->chunk_used =
+ memcmp(&spare_ff, &spare, sizeof(spare_ff)) ? 1 : 0;
+
+ if (ext_tags->chunk_used) {
+ yaffs_get_tags_from_spare(dev, &spare, &tags);
+ ext_tags->obj_id = tags.obj_id;
+ ext_tags->chunk_id = tags.chunk_id;
+ ext_tags->n_bytes = tags.n_bytes_lsb;
+
+ if (dev->data_bytes_per_chunk >= 1024)
+ ext_tags->n_bytes |=
+ (((unsigned)tags.n_bytes_msb) << 10);
+
+ ext_tags->serial_number = tags.serial_number;
+ }
+
+ return YAFFS_OK;
+}
+
+int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int flash_block)
+{
+ struct yaffs_spare spare;
+
+ memset(&spare, 0xff, sizeof(struct yaffs_spare));
+
+ spare.block_status = 'Y';
+
+ yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block, NULL,
+ &spare);
+ yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block + 1,
+ NULL, &spare);
+
+ return YAFFS_OK;
+}
+
+int yaffs_tags_compat_query_block(struct yaffs_dev *dev,
+ int block_no,
+ enum yaffs_block_state *state,
+ u32 *seq_number)
+{
+ struct yaffs_spare spare0, spare1;
+ static struct yaffs_spare spare_ff;
+ static int init;
+ enum yaffs_ecc_result dummy;
+
+ if (!init) {
+ memset(&spare_ff, 0xff, sizeof(spare_ff));
+ init = 1;
+ }
+
+ *seq_number = 0;
+
+ yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block, NULL,
+ &spare0, &dummy, 1);
+ yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block + 1,
+ NULL, &spare1, &dummy, 1);
+
+ if (hweight8(spare0.block_status & spare1.block_status) < 7)
+ *state = YAFFS_BLOCK_STATE_DEAD;
+ else if (memcmp(&spare_ff, &spare0, sizeof(spare_ff)) == 0)
+ *state = YAFFS_BLOCK_STATE_EMPTY;
+ else
+ *state = YAFFS_BLOCK_STATE_NEEDS_SCAN;
+
+ return YAFFS_OK;
+}
diff --git a/roms/u-boot/fs/yaffs2/yaffs_tagscompat.h b/roms/u-boot/fs/yaffs2/yaffs_tagscompat.h
new file mode 100644
index 000000000..b3c665577
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_tagscompat.h
@@ -0,0 +1,36 @@
+/*
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ */
+
+#ifndef __YAFFS_TAGSCOMPAT_H__
+#define __YAFFS_TAGSCOMPAT_H__
+
+#include "yaffs_guts.h"
+int yaffs_tags_compat_wr(struct yaffs_dev *dev,
+ int nand_chunk,
+ const u8 *data, const struct yaffs_ext_tags *tags);
+int yaffs_tags_compat_rd(struct yaffs_dev *dev,
+ int nand_chunk,
+ u8 *data, struct yaffs_ext_tags *tags);
+int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int block_no);
+int yaffs_tags_compat_query_block(struct yaffs_dev *dev,
+ int block_no,
+ enum yaffs_block_state *state,
+ u32 *seq_number);
+
+void yaffs_calc_tags_ecc(struct yaffs_tags *tags);
+int yaffs_check_tags_ecc(struct yaffs_tags *tags);
+int yaffs_count_bits(u8 byte);
+
+#endif
diff --git a/roms/u-boot/fs/yaffs2/yaffs_trace.h b/roms/u-boot/fs/yaffs2/yaffs_trace.h
new file mode 100644
index 000000000..fd26054d3
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_trace.h
@@ -0,0 +1,57 @@
+/*
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ */
+
+#ifndef __YTRACE_H__
+#define __YTRACE_H__
+
+extern unsigned int yaffs_trace_mask;
+extern unsigned int yaffs_wr_attempts;
+
+/*
+ * Tracing flags.
+ * The flags masked in YAFFS_TRACE_ALWAYS are always traced.
+ */
+
+#define YAFFS_TRACE_OS 0x00000002
+#define YAFFS_TRACE_ALLOCATE 0x00000004
+#define YAFFS_TRACE_SCAN 0x00000008
+#define YAFFS_TRACE_BAD_BLOCKS 0x00000010
+#define YAFFS_TRACE_ERASE 0x00000020
+#define YAFFS_TRACE_GC 0x00000040
+#define YAFFS_TRACE_WRITE 0x00000080
+#define YAFFS_TRACE_TRACING 0x00000100
+#define YAFFS_TRACE_DELETION 0x00000200
+#define YAFFS_TRACE_BUFFERS 0x00000400
+#define YAFFS_TRACE_NANDACCESS 0x00000800
+#define YAFFS_TRACE_GC_DETAIL 0x00001000
+#define YAFFS_TRACE_SCAN_DEBUG 0x00002000
+#define YAFFS_TRACE_MTD 0x00004000
+#define YAFFS_TRACE_CHECKPOINT 0x00008000
+
+#define YAFFS_TRACE_VERIFY 0x00010000
+#define YAFFS_TRACE_VERIFY_NAND 0x00020000
+#define YAFFS_TRACE_VERIFY_FULL 0x00040000
+#define YAFFS_TRACE_VERIFY_ALL 0x000f0000
+
+#define YAFFS_TRACE_SYNC 0x00100000
+#define YAFFS_TRACE_BACKGROUND 0x00200000
+#define YAFFS_TRACE_LOCK 0x00400000
+#define YAFFS_TRACE_MOUNT 0x00800000
+
+#define YAFFS_TRACE_ERROR 0x40000000
+#define YAFFS_TRACE_BUG 0x80000000
+#define YAFFS_TRACE_ALWAYS 0xf0000000
+
+#endif
diff --git a/roms/u-boot/fs/yaffs2/yaffs_uboot_glue.c b/roms/u-boot/fs/yaffs2/yaffs_uboot_glue.c
new file mode 100644
index 000000000..7a15a0297
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_uboot_glue.c
@@ -0,0 +1,466 @@
+/*
+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2007 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/*
+ * yaffscfg.c The configuration for the "direct" use of yaffs.
+ *
+ * This is set up for u-boot.
+ *
+ * This version now uses the ydevconfig mechanism to set up partitions.
+ */
+
+#include <common.h>
+#include <div64.h>
+#include <malloc.h>
+
+#include <config.h>
+#include "nand.h"
+#include "yaffscfg.h"
+#include "yaffsfs.h"
+#include "yaffs_packedtags2.h"
+#include "yaffs_mtdif.h"
+#include "yaffs_mtdif2.h"
+#if 0
+#include <errno.h>
+#else
+#include "malloc.h"
+#endif
+
+unsigned yaffs_trace_mask = 0x0; /* Disable logging */
+static int yaffs_errno;
+
+
+void yaffs_bug_fn(const char *fn, int n)
+{
+ printf("yaffs bug at %s:%d\n", fn, n);
+}
+
+void *yaffsfs_malloc(size_t x)
+{
+ return malloc(x);
+}
+
+void yaffsfs_free(void *x)
+{
+ free(x);
+}
+
+void yaffsfs_SetError(int err)
+{
+ yaffs_errno = err;
+}
+
+int yaffsfs_GetLastError(void)
+{
+ return yaffs_errno;
+}
+
+
+int yaffsfs_GetError(void)
+{
+ return yaffs_errno;
+}
+
+void yaffsfs_Lock(void)
+{
+}
+
+void yaffsfs_Unlock(void)
+{
+}
+
+__u32 yaffsfs_CurrentTime(void)
+{
+ return 0;
+}
+
+void *yaffs_malloc(size_t size)
+{
+ return malloc(size);
+}
+
+void yaffs_free(void *ptr)
+{
+ free(ptr);
+}
+
+void yaffsfs_LocalInitialisation(void)
+{
+ /* No locking used */
+}
+
+
+static const char *yaffs_file_type_str(struct yaffs_stat *stat)
+{
+ switch (stat->st_mode & S_IFMT) {
+ case S_IFREG: return "regular file";
+ case S_IFDIR: return "directory";
+ case S_IFLNK: return "symlink";
+ default: return "unknown";
+ }
+}
+
+static const char *yaffs_error_str(void)
+{
+ int error = yaffsfs_GetLastError();
+
+ if (error < 0)
+ error = -error;
+
+ switch (error) {
+ case EBUSY: return "Busy";
+ case ENODEV: return "No such device";
+ case EINVAL: return "Invalid parameter";
+ case ENFILE: return "Too many open files";
+ case EBADF: return "Bad handle";
+ case EACCES: return "Wrong permissions";
+ case EXDEV: return "Not on same device";
+ case ENOENT: return "No such entry";
+ case ENOSPC: return "Device full";
+ case EROFS: return "Read only file system";
+ case ERANGE: return "Range error";
+ case ENOTEMPTY: return "Not empty";
+ case ENAMETOOLONG: return "Name too long";
+ case ENOMEM: return "Out of memory";
+ case EFAULT: return "Fault";
+ case EEXIST: return "Name exists";
+ case ENOTDIR: return "Not a directory";
+ case EISDIR: return "Not permitted on a directory";
+ case ELOOP: return "Symlink loop";
+ case 0: return "No error";
+ default: return "Unknown error";
+ }
+}
+
+void cmd_yaffs_tracemask(unsigned set, unsigned mask)
+{
+ if (set)
+ yaffs_trace_mask = mask;
+
+ printf("yaffs trace mask: %08x\n", yaffs_trace_mask);
+}
+
+static int yaffs_regions_overlap(int a, int b, int x, int y)
+{
+ return (a <= x && x <= b) ||
+ (a <= y && y <= b) ||
+ (x <= a && a <= y) ||
+ (x <= b && b <= y);
+}
+
+void cmd_yaffs_devconfig(char *_mp, int flash_dev,
+ int start_block, int end_block)
+{
+ struct mtd_info *mtd = NULL;
+ struct yaffs_dev *dev = NULL;
+ struct yaffs_dev *chk;
+ char *mp = NULL;
+ struct nand_chip *chip;
+
+ mtd = get_nand_dev_by_index(flash_dev);
+ if (!mtd) {
+ pr_err("\nno NAND devices available\n");
+ return;
+ }
+
+ dev = calloc(1, sizeof(*dev));
+ mp = strdup(_mp);
+
+ if (!dev || !mp) {
+ /* Alloc error */
+ printf("Failed to allocate memory\n");
+ goto err;
+ }
+
+ if (flash_dev >= CONFIG_SYS_MAX_NAND_DEVICE) {
+ printf("Flash device invalid\n");
+ goto err;
+ }
+
+ if (end_block == 0)
+ end_block = lldiv(mtd->size, mtd->erasesize - 1);
+
+ if (end_block < start_block) {
+ printf("Bad start/end\n");
+ goto err;
+ }
+
+ chip = mtd_to_nand(mtd);
+
+ /* Check for any conflicts */
+ yaffs_dev_rewind();
+ while (1) {
+ chk = yaffs_next_dev();
+ if (!chk)
+ break;
+ if (strcmp(chk->param.name, mp) == 0) {
+ printf("Mount point name already used\n");
+ goto err;
+ }
+ if (chk->driver_context == mtd &&
+ yaffs_regions_overlap(
+ chk->param.start_block, chk->param.end_block,
+ start_block, end_block)) {
+ printf("Region overlaps with partition %s\n",
+ chk->param.name);
+ goto err;
+ }
+
+ }
+
+ /* Seems sane, so configure */
+ memset(dev, 0, sizeof(*dev));
+ dev->param.name = mp;
+ dev->driver_context = mtd;
+ dev->param.start_block = start_block;
+ dev->param.end_block = end_block;
+ dev->param.chunks_per_block = mtd->erasesize / mtd->writesize;
+ dev->param.total_bytes_per_chunk = mtd->writesize;
+ dev->param.is_yaffs2 = 1;
+ dev->param.use_nand_ecc = 1;
+ dev->param.n_reserved_blocks = 5;
+ if (chip->ecc.layout->oobavail < sizeof(struct yaffs_packed_tags2))
+ dev->param.inband_tags = 1;
+ dev->param.n_caches = 10;
+ dev->param.write_chunk_tags_fn = nandmtd2_write_chunk_tags;
+ dev->param.read_chunk_tags_fn = nandmtd2_read_chunk_tags;
+ dev->param.erase_fn = nandmtd_EraseBlockInNAND;
+ dev->param.initialise_flash_fn = nandmtd_InitialiseNAND;
+ dev->param.bad_block_fn = nandmtd2_MarkNANDBlockBad;
+ dev->param.query_block_fn = nandmtd2_QueryNANDBlock;
+
+ yaffs_add_device(dev);
+
+ printf("Configures yaffs mount %s: dev %d start block %d, end block %d %s\n",
+ mp, flash_dev, start_block, end_block,
+ dev->param.inband_tags ? "using inband tags" : "");
+ return;
+
+err:
+ free(dev);
+ free(mp);
+}
+
+void cmd_yaffs_dev_ls(void)
+{
+ struct yaffs_dev *dev;
+ int flash_dev;
+ int free_space;
+
+ yaffs_dev_rewind();
+
+ while (1) {
+ dev = yaffs_next_dev();
+ if (!dev)
+ return;
+ flash_dev = nand_mtd_to_devnum(dev->driver_context);
+ printf("%-10s %5d 0x%05x 0x%05x %s",
+ dev->param.name, flash_dev,
+ dev->param.start_block, dev->param.end_block,
+ dev->param.inband_tags ? "using inband tags, " : "");
+
+ free_space = yaffs_freespace(dev->param.name);
+ if (free_space < 0)
+ printf("not mounted\n");
+ else
+ printf("free 0x%x\n", free_space);
+
+ }
+}
+
+void make_a_file(char *yaffsName, char bval, int sizeOfFile)
+{
+ int outh;
+ int i;
+ unsigned char buffer[100];
+
+ outh = yaffs_open(yaffsName,
+ O_CREAT | O_RDWR | O_TRUNC,
+ S_IREAD | S_IWRITE);
+ if (outh < 0) {
+ printf("Error opening file: %d. %s\n", outh, yaffs_error_str());
+ return;
+ }
+
+ memset(buffer, bval, 100);
+
+ do {
+ i = sizeOfFile;
+ if (i > 100)
+ i = 100;
+ sizeOfFile -= i;
+
+ yaffs_write(outh, buffer, i);
+
+ } while (sizeOfFile > 0);
+
+
+ yaffs_close(outh);
+}
+
+void read_a_file(char *fn)
+{
+ int h;
+ int i = 0;
+ unsigned char b;
+
+ h = yaffs_open(fn, O_RDWR, 0);
+ if (h < 0) {
+ printf("File not found\n");
+ return;
+ }
+
+ while (yaffs_read(h, &b, 1) > 0) {
+ printf("%02x ", b);
+ i++;
+ if (i > 32) {
+ printf("\n");
+ i = 0;
+ }
+ }
+ printf("\n");
+ yaffs_close(h);
+}
+
+void cmd_yaffs_mount(char *mp)
+{
+ int retval = yaffs_mount(mp);
+ if (retval < 0)
+ printf("Error mounting %s, return value: %d, %s\n", mp,
+ yaffsfs_GetError(), yaffs_error_str());
+}
+
+
+void cmd_yaffs_umount(char *mp)
+{
+ if (yaffs_unmount(mp) == -1)
+ printf("Error umounting %s, return value: %d, %s\n", mp,
+ yaffsfs_GetError(), yaffs_error_str());
+}
+
+void cmd_yaffs_write_file(char *yaffsName, char bval, int sizeOfFile)
+{
+ make_a_file(yaffsName, bval, sizeOfFile);
+}
+
+
+void cmd_yaffs_read_file(char *fn)
+{
+ read_a_file(fn);
+}
+
+
+void cmd_yaffs_mread_file(char *fn, char *addr)
+{
+ int h;
+ struct yaffs_stat s;
+
+ yaffs_stat(fn, &s);
+
+ printf("Copy %s to 0x%p... ", fn, addr);
+ h = yaffs_open(fn, O_RDWR, 0);
+ if (h < 0) {
+ printf("File not found\n");
+ return;
+ }
+
+ yaffs_read(h, addr, (int)s.st_size);
+ printf("\t[DONE]\n");
+
+ yaffs_close(h);
+}
+
+
+void cmd_yaffs_mwrite_file(char *fn, char *addr, int size)
+{
+ int outh;
+
+ outh = yaffs_open(fn, O_CREAT | O_RDWR | O_TRUNC, S_IREAD | S_IWRITE);
+ if (outh < 0)
+ printf("Error opening file: %d, %s\n", outh, yaffs_error_str());
+
+ yaffs_write(outh, addr, size);
+
+ yaffs_close(outh);
+}
+
+
+void cmd_yaffs_ls(const char *mountpt, int longlist)
+{
+ int i;
+ yaffs_DIR *d;
+ struct yaffs_dirent *de;
+ struct yaffs_stat stat;
+ char tempstr[255];
+
+ d = yaffs_opendir(mountpt);
+
+ if (!d) {
+ printf("opendir failed, %s\n", yaffs_error_str());
+ return;
+ }
+
+ for (i = 0; (de = yaffs_readdir(d)) != NULL; i++) {
+ if (longlist) {
+ sprintf(tempstr, "%s/%s", mountpt, de->d_name);
+ yaffs_lstat(tempstr, &stat);
+ printf("%-25s\t%7ld",
+ de->d_name,
+ (long)stat.st_size);
+ printf(" %5d %s\n",
+ stat.st_ino,
+ yaffs_file_type_str(&stat));
+ } else {
+ printf("%s\n", de->d_name);
+ }
+ }
+
+ yaffs_closedir(d);
+}
+
+
+void cmd_yaffs_mkdir(const char *dir)
+{
+ int retval = yaffs_mkdir(dir, 0);
+
+ if (retval < 0)
+ printf("yaffs_mkdir returning error: %d, %s\n",
+ retval, yaffs_error_str());
+}
+
+void cmd_yaffs_rmdir(const char *dir)
+{
+ int retval = yaffs_rmdir(dir);
+
+ if (retval < 0)
+ printf("yaffs_rmdir returning error: %d, %s\n",
+ retval, yaffs_error_str());
+}
+
+void cmd_yaffs_rm(const char *path)
+{
+ int retval = yaffs_unlink(path);
+
+ if (retval < 0)
+ printf("yaffs_unlink returning error: %d, %s\n",
+ retval, yaffs_error_str());
+}
+
+void cmd_yaffs_mv(const char *oldPath, const char *newPath)
+{
+ int retval = yaffs_rename(newPath, oldPath);
+
+ if (retval < 0)
+ printf("yaffs_unlink returning error: %d, %s\n",
+ retval, yaffs_error_str());
+}
diff --git a/roms/u-boot/fs/yaffs2/yaffs_verify.c b/roms/u-boot/fs/yaffs2/yaffs_verify.c
new file mode 100644
index 000000000..3fef28bdc
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_verify.c
@@ -0,0 +1,522 @@
+/*
+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "yaffs_verify.h"
+#include "yaffs_trace.h"
+#include "yaffs_bitmap.h"
+#include "yaffs_getblockinfo.h"
+#include "yaffs_nand.h"
+
+int yaffs_skip_verification(struct yaffs_dev *dev)
+{
+ return !(yaffs_trace_mask &
+ (YAFFS_TRACE_VERIFY | YAFFS_TRACE_VERIFY_FULL));
+}
+
+static int yaffs_skip_full_verification(struct yaffs_dev *dev)
+{
+ return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_FULL));
+}
+
+static int yaffs_skip_nand_verification(struct yaffs_dev *dev)
+{
+ return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_NAND));
+}
+
+static const char * const block_state_name[] = {
+ "Unknown",
+ "Needs scan",
+ "Scanning",
+ "Empty",
+ "Allocating",
+ "Full",
+ "Dirty",
+ "Checkpoint",
+ "Collecting",
+ "Dead"
+};
+
+void yaffs_verify_blk(struct yaffs_dev *dev, struct yaffs_block_info *bi, int n)
+{
+ int actually_used;
+ int in_use;
+
+ if (yaffs_skip_verification(dev))
+ return;
+
+ /* Report illegal runtime states */
+ if (bi->block_state >= YAFFS_NUMBER_OF_BLOCK_STATES)
+ yaffs_trace(YAFFS_TRACE_VERIFY,
+ "Block %d has undefined state %d",
+ n, bi->block_state);
+
+ switch (bi->block_state) {
+ case YAFFS_BLOCK_STATE_UNKNOWN:
+ case YAFFS_BLOCK_STATE_SCANNING:
+ case YAFFS_BLOCK_STATE_NEEDS_SCAN:
+ yaffs_trace(YAFFS_TRACE_VERIFY,
+ "Block %d has bad run-state %s",
+ n, block_state_name[bi->block_state]);
+ }
+
+ /* Check pages in use and soft deletions are legal */
+
+ actually_used = bi->pages_in_use - bi->soft_del_pages;
+
+ if (bi->pages_in_use < 0 ||
+ bi->pages_in_use > dev->param.chunks_per_block ||
+ bi->soft_del_pages < 0 ||
+ bi->soft_del_pages > dev->param.chunks_per_block ||
+ actually_used < 0 || actually_used > dev->param.chunks_per_block)
+ yaffs_trace(YAFFS_TRACE_VERIFY,
+ "Block %d has illegal values pages_in_used %d soft_del_pages %d",
+ n, bi->pages_in_use, bi->soft_del_pages);
+
+ /* Check chunk bitmap legal */
+ in_use = yaffs_count_chunk_bits(dev, n);
+ if (in_use != bi->pages_in_use)
+ yaffs_trace(YAFFS_TRACE_VERIFY,
+ "Block %d has inconsistent values pages_in_use %d counted chunk bits %d",
+ n, bi->pages_in_use, in_use);
+}
+
+void yaffs_verify_collected_blk(struct yaffs_dev *dev,
+ struct yaffs_block_info *bi, int n)
+{
+ yaffs_verify_blk(dev, bi, n);
+
+ /* After collection the block should be in the erased state */
+
+ if (bi->block_state != YAFFS_BLOCK_STATE_COLLECTING &&
+ bi->block_state != YAFFS_BLOCK_STATE_EMPTY) {
+ yaffs_trace(YAFFS_TRACE_ERROR,
+ "Block %d is in state %d after gc, should be erased",
+ n, bi->block_state);
+ }
+}
+
+void yaffs_verify_blocks(struct yaffs_dev *dev)
+{
+ int i;
+ int state_count[YAFFS_NUMBER_OF_BLOCK_STATES];
+ int illegal_states = 0;
+
+ if (yaffs_skip_verification(dev))
+ return;
+
+ memset(state_count, 0, sizeof(state_count));
+
+ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) {
+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, i);
+ yaffs_verify_blk(dev, bi, i);
+
+ if (bi->block_state < YAFFS_NUMBER_OF_BLOCK_STATES)
+ state_count[bi->block_state]++;
+ else
+ illegal_states++;
+ }
+
+ yaffs_trace(YAFFS_TRACE_VERIFY, "Block summary");
+
+ yaffs_trace(YAFFS_TRACE_VERIFY,
+ "%d blocks have illegal states",
+ illegal_states);
+ if (state_count[YAFFS_BLOCK_STATE_ALLOCATING] > 1)
+ yaffs_trace(YAFFS_TRACE_VERIFY,
+ "Too many allocating blocks");
+
+ for (i = 0; i < YAFFS_NUMBER_OF_BLOCK_STATES; i++)
+ yaffs_trace(YAFFS_TRACE_VERIFY,
+ "%s %d blocks",
+ block_state_name[i], state_count[i]);
+
+ if (dev->blocks_in_checkpt != state_count[YAFFS_BLOCK_STATE_CHECKPOINT])
+ yaffs_trace(YAFFS_TRACE_VERIFY,
+ "Checkpoint block count wrong dev %d count %d",
+ dev->blocks_in_checkpt,
+ state_count[YAFFS_BLOCK_STATE_CHECKPOINT]);
+
+ if (dev->n_erased_blocks != state_count[YAFFS_BLOCK_STATE_EMPTY])
+ yaffs_trace(YAFFS_TRACE_VERIFY,
+ "Erased block count wrong dev %d count %d",
+ dev->n_erased_blocks,
+ state_count[YAFFS_BLOCK_STATE_EMPTY]);
+
+ if (state_count[YAFFS_BLOCK_STATE_COLLECTING] > 1)
+ yaffs_trace(YAFFS_TRACE_VERIFY,
+ "Too many collecting blocks %d (max is 1)",
+ state_count[YAFFS_BLOCK_STATE_COLLECTING]);
+}
+
+/*
+ * Verify the object header. oh must be valid, but obj and tags may be NULL in
+ * which case those tests will not be performed.
+ */
+void yaffs_verify_oh(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh,
+ struct yaffs_ext_tags *tags, int parent_check)
+{
+ if (obj && yaffs_skip_verification(obj->my_dev))
+ return;
+
+ if (!(tags && obj && oh)) {
+ yaffs_trace(YAFFS_TRACE_VERIFY,
+ "Verifying object header tags %p obj %p oh %p",
+ tags, obj, oh);
+ return;
+ }
+
+ if (oh->type <= YAFFS_OBJECT_TYPE_UNKNOWN ||
+ oh->type > YAFFS_OBJECT_TYPE_MAX)
+ yaffs_trace(YAFFS_TRACE_VERIFY,
+ "Obj %d header type is illegal value 0x%x",
+ tags->obj_id, oh->type);
+
+ if (tags->obj_id != obj->obj_id)
+ yaffs_trace(YAFFS_TRACE_VERIFY,
+ "Obj %d header mismatch obj_id %d",
+ tags->obj_id, obj->obj_id);
+
+ /*
+ * Check that the object's parent ids match if parent_check requested.
+ *
+ * Tests do not apply to the root object.
+ */
+
+ if (parent_check && tags->obj_id > 1 && !obj->parent)
+ yaffs_trace(YAFFS_TRACE_VERIFY,
+ "Obj %d header mismatch parent_id %d obj->parent is NULL",
+ tags->obj_id, oh->parent_obj_id);
+
+ if (parent_check && obj->parent &&
+ oh->parent_obj_id != obj->parent->obj_id &&
+ (oh->parent_obj_id != YAFFS_OBJECTID_UNLINKED ||
+ obj->parent->obj_id != YAFFS_OBJECTID_DELETED))
+ yaffs_trace(YAFFS_TRACE_VERIFY,
+ "Obj %d header mismatch parent_id %d parent_obj_id %d",
+ tags->obj_id, oh->parent_obj_id,
+ obj->parent->obj_id);
+
+ if (tags->obj_id > 1 && oh->name[0] == 0) /* Null name */
+ yaffs_trace(YAFFS_TRACE_VERIFY,
+ "Obj %d header name is NULL",
+ obj->obj_id);
+
+ if (tags->obj_id > 1 && ((u8) (oh->name[0])) == 0xff) /* Junk name */
+ yaffs_trace(YAFFS_TRACE_VERIFY,
+ "Obj %d header name is 0xff",
+ obj->obj_id);
+}
+
+void yaffs_verify_file(struct yaffs_obj *obj)
+{
+ u32 x;
+ int required_depth;
+ int last_chunk;
+ u32 offset_in_chunk;
+ u32 the_chunk;
+
+ u32 i;
+ struct yaffs_dev *dev;
+ struct yaffs_ext_tags tags;
+ struct yaffs_tnode *tn;
+ u32 obj_id;
+
+ if (!obj)
+ return;
+
+ if (yaffs_skip_verification(obj->my_dev))
+ return;
+
+ dev = obj->my_dev;
+ obj_id = obj->obj_id;
+
+
+ /* Check file size is consistent with tnode depth */
+ yaffs_addr_to_chunk(dev, obj->variant.file_variant.file_size,
+ &last_chunk, &offset_in_chunk);
+ last_chunk++;
+ x = last_chunk >> YAFFS_TNODES_LEVEL0_BITS;
+ required_depth = 0;
+ while (x > 0) {
+ x >>= YAFFS_TNODES_INTERNAL_BITS;
+ required_depth++;
+ }
+
+ /* Check that the chunks in the tnode tree are all correct.
+ * We do this by scanning through the tnode tree and
+ * checking the tags for every chunk match.
+ */
+
+ if (yaffs_skip_nand_verification(dev))
+ return;
+
+ for (i = 1; i <= last_chunk; i++) {
+ tn = yaffs_find_tnode_0(dev, &obj->variant.file_variant, i);
+
+ if (!tn)
+ continue;
+
+ the_chunk = yaffs_get_group_base(dev, tn, i);
+ if (the_chunk > 0) {
+ yaffs_rd_chunk_tags_nand(dev, the_chunk, NULL,
+ &tags);
+ if (tags.obj_id != obj_id || tags.chunk_id != i)
+ yaffs_trace(YAFFS_TRACE_VERIFY,
+ "Object %d chunk_id %d NAND mismatch chunk %d tags (%d:%d)",
+ obj_id, i, the_chunk,
+ tags.obj_id, tags.chunk_id);
+ }
+ }
+}
+
+void yaffs_verify_link(struct yaffs_obj *obj)
+{
+ if (obj && yaffs_skip_verification(obj->my_dev))
+ return;
+
+ /* Verify sane equivalent object */
+}
+
+void yaffs_verify_symlink(struct yaffs_obj *obj)
+{
+ if (obj && yaffs_skip_verification(obj->my_dev))
+ return;
+
+ /* Verify symlink string */
+}
+
+void yaffs_verify_special(struct yaffs_obj *obj)
+{
+ if (obj && yaffs_skip_verification(obj->my_dev))
+ return;
+}
+
+void yaffs_verify_obj(struct yaffs_obj *obj)
+{
+ struct yaffs_dev *dev;
+ u32 chunk_min;
+ u32 chunk_max;
+ u32 chunk_id_ok;
+ u32 chunk_in_range;
+ u32 chunk_wrongly_deleted;
+ u32 chunk_valid;
+
+ if (!obj)
+ return;
+
+ if (obj->being_created)
+ return;
+
+ dev = obj->my_dev;
+
+ if (yaffs_skip_verification(dev))
+ return;
+
+ /* Check sane object header chunk */
+
+ chunk_min = dev->internal_start_block * dev->param.chunks_per_block;
+ chunk_max =
+ (dev->internal_end_block + 1) * dev->param.chunks_per_block - 1;
+
+ chunk_in_range = (((unsigned)(obj->hdr_chunk)) >= chunk_min &&
+ ((unsigned)(obj->hdr_chunk)) <= chunk_max);
+ chunk_id_ok = chunk_in_range || (obj->hdr_chunk == 0);
+ chunk_valid = chunk_in_range &&
+ yaffs_check_chunk_bit(dev,
+ obj->hdr_chunk / dev->param.chunks_per_block,
+ obj->hdr_chunk % dev->param.chunks_per_block);
+ chunk_wrongly_deleted = chunk_in_range && !chunk_valid;
+
+ if (!obj->fake && (!chunk_id_ok || chunk_wrongly_deleted))
+ yaffs_trace(YAFFS_TRACE_VERIFY,
+ "Obj %d has chunk_id %d %s %s",
+ obj->obj_id, obj->hdr_chunk,
+ chunk_id_ok ? "" : ",out of range",
+ chunk_wrongly_deleted ? ",marked as deleted" : "");
+
+ if (chunk_valid && !yaffs_skip_nand_verification(dev)) {
+ struct yaffs_ext_tags tags;
+ struct yaffs_obj_hdr *oh;
+ u8 *buffer = yaffs_get_temp_buffer(dev);
+
+ oh = (struct yaffs_obj_hdr *)buffer;
+
+ yaffs_rd_chunk_tags_nand(dev, obj->hdr_chunk, buffer, &tags);
+
+ yaffs_verify_oh(obj, oh, &tags, 1);
+
+ yaffs_release_temp_buffer(dev, buffer);
+ }
+
+ /* Verify it has a parent */
+ if (obj && !obj->fake && (!obj->parent || obj->parent->my_dev != dev)) {
+ yaffs_trace(YAFFS_TRACE_VERIFY,
+ "Obj %d has parent pointer %p which does not look like an object",
+ obj->obj_id, obj->parent);
+ }
+
+ /* Verify parent is a directory */
+ if (obj->parent &&
+ obj->parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {
+ yaffs_trace(YAFFS_TRACE_VERIFY,
+ "Obj %d's parent is not a directory (type %d)",
+ obj->obj_id, obj->parent->variant_type);
+ }
+
+ switch (obj->variant_type) {
+ case YAFFS_OBJECT_TYPE_FILE:
+ yaffs_verify_file(obj);
+ break;
+ case YAFFS_OBJECT_TYPE_SYMLINK:
+ yaffs_verify_symlink(obj);
+ break;
+ case YAFFS_OBJECT_TYPE_DIRECTORY:
+ yaffs_verify_dir(obj);
+ break;
+ case YAFFS_OBJECT_TYPE_HARDLINK:
+ yaffs_verify_link(obj);
+ break;
+ case YAFFS_OBJECT_TYPE_SPECIAL:
+ yaffs_verify_special(obj);
+ break;
+ case YAFFS_OBJECT_TYPE_UNKNOWN:
+ default:
+ yaffs_trace(YAFFS_TRACE_VERIFY,
+ "Obj %d has illegaltype %d",
+ obj->obj_id, obj->variant_type);
+ break;
+ }
+}
+
+void yaffs_verify_objects(struct yaffs_dev *dev)
+{
+ struct yaffs_obj *obj;
+ int i;
+ struct list_head *lh;
+
+ if (yaffs_skip_verification(dev))
+ return;
+
+ /* Iterate through the objects in each hash entry */
+
+ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) {
+ list_for_each(lh, &dev->obj_bucket[i].list) {
+ obj = list_entry(lh, struct yaffs_obj, hash_link);
+ yaffs_verify_obj(obj);
+ }
+ }
+}
+
+void yaffs_verify_obj_in_dir(struct yaffs_obj *obj)
+{
+ struct list_head *lh;
+ struct yaffs_obj *list_obj;
+ int count = 0;
+
+ if (!obj) {
+ yaffs_trace(YAFFS_TRACE_ALWAYS, "No object to verify");
+ BUG();
+ return;
+ }
+
+ if (yaffs_skip_verification(obj->my_dev))
+ return;
+
+ if (!obj->parent) {
+ yaffs_trace(YAFFS_TRACE_ALWAYS, "Object does not have parent");
+ BUG();
+ return;
+ }
+
+ if (obj->parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {
+ yaffs_trace(YAFFS_TRACE_ALWAYS, "Parent is not directory");
+ BUG();
+ }
+
+ /* Iterate through the objects in each hash entry */
+
+ list_for_each(lh, &obj->parent->variant.dir_variant.children) {
+ list_obj = list_entry(lh, struct yaffs_obj, siblings);
+ yaffs_verify_obj(list_obj);
+ if (obj == list_obj)
+ count++;
+ }
+
+ if (count != 1) {
+ yaffs_trace(YAFFS_TRACE_ALWAYS,
+ "Object in directory %d times",
+ count);
+ BUG();
+ }
+}
+
+void yaffs_verify_dir(struct yaffs_obj *directory)
+{
+ struct list_head *lh;
+ struct yaffs_obj *list_obj;
+
+ if (!directory) {
+ BUG();
+ return;
+ }
+
+ if (yaffs_skip_full_verification(directory->my_dev))
+ return;
+
+ if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {
+ yaffs_trace(YAFFS_TRACE_ALWAYS,
+ "Directory has wrong type: %d",
+ directory->variant_type);
+ BUG();
+ }
+
+ /* Iterate through the objects in each hash entry */
+
+ list_for_each(lh, &directory->variant.dir_variant.children) {
+ list_obj = list_entry(lh, struct yaffs_obj, siblings);
+ if (list_obj->parent != directory) {
+ yaffs_trace(YAFFS_TRACE_ALWAYS,
+ "Object in directory list has wrong parent %p",
+ list_obj->parent);
+ BUG();
+ }
+ yaffs_verify_obj_in_dir(list_obj);
+ }
+}
+
+static int yaffs_free_verification_failures;
+
+void yaffs_verify_free_chunks(struct yaffs_dev *dev)
+{
+ int counted;
+ int difference;
+
+ if (yaffs_skip_verification(dev))
+ return;
+
+ counted = yaffs_count_free_chunks(dev);
+
+ difference = dev->n_free_chunks - counted;
+
+ if (difference) {
+ yaffs_trace(YAFFS_TRACE_ALWAYS,
+ "Freechunks verification failure %d %d %d",
+ dev->n_free_chunks, counted, difference);
+ yaffs_free_verification_failures++;
+ }
+}
+
+int yaffs_verify_file_sane(struct yaffs_obj *in)
+{
+ return YAFFS_OK;
+}
diff --git a/roms/u-boot/fs/yaffs2/yaffs_verify.h b/roms/u-boot/fs/yaffs2/yaffs_verify.h
new file mode 100644
index 000000000..4f4af8d29
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_verify.h
@@ -0,0 +1,43 @@
+/*
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ */
+
+#ifndef __YAFFS_VERIFY_H__
+#define __YAFFS_VERIFY_H__
+
+#include "yaffs_guts.h"
+
+void yaffs_verify_blk(struct yaffs_dev *dev, struct yaffs_block_info *bi,
+ int n);
+void yaffs_verify_collected_blk(struct yaffs_dev *dev,
+ struct yaffs_block_info *bi, int n);
+void yaffs_verify_blocks(struct yaffs_dev *dev);
+
+void yaffs_verify_oh(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh,
+ struct yaffs_ext_tags *tags, int parent_check);
+void yaffs_verify_file(struct yaffs_obj *obj);
+void yaffs_verify_link(struct yaffs_obj *obj);
+void yaffs_verify_symlink(struct yaffs_obj *obj);
+void yaffs_verify_special(struct yaffs_obj *obj);
+void yaffs_verify_obj(struct yaffs_obj *obj);
+void yaffs_verify_objects(struct yaffs_dev *dev);
+void yaffs_verify_obj_in_dir(struct yaffs_obj *obj);
+void yaffs_verify_dir(struct yaffs_obj *directory);
+void yaffs_verify_free_chunks(struct yaffs_dev *dev);
+
+int yaffs_verify_file_sane(struct yaffs_obj *obj);
+
+int yaffs_skip_verification(struct yaffs_dev *dev);
+
+#endif
diff --git a/roms/u-boot/fs/yaffs2/yaffs_yaffs1.c b/roms/u-boot/fs/yaffs2/yaffs_yaffs1.c
new file mode 100644
index 000000000..8c176b982
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_yaffs1.c
@@ -0,0 +1,420 @@
+/*
+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "yaffs_yaffs1.h"
+#include "yportenv.h"
+#include "yaffs_trace.h"
+#include "yaffs_bitmap.h"
+#include "yaffs_getblockinfo.h"
+#include "yaffs_nand.h"
+#include "yaffs_attribs.h"
+#include <dm/devres.h>
+
+int yaffs1_scan(struct yaffs_dev *dev)
+{
+ struct yaffs_ext_tags tags;
+ int blk;
+ int chunk;
+ int c;
+ int deleted;
+ enum yaffs_block_state state;
+ LIST_HEAD(hard_list);
+ struct yaffs_block_info *bi;
+ u32 seq_number;
+ struct yaffs_obj_hdr *oh;
+ struct yaffs_obj *in;
+ struct yaffs_obj *parent;
+ int alloc_failed = 0;
+ struct yaffs_shadow_fixer *shadow_fixers = NULL;
+ u8 *chunk_data;
+
+ yaffs_trace(YAFFS_TRACE_SCAN,
+ "yaffs1_scan starts intstartblk %d intendblk %d...",
+ dev->internal_start_block, dev->internal_end_block);
+
+ chunk_data = yaffs_get_temp_buffer(dev);
+
+ dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER;
+
+ /* Scan all the blocks to determine their state */
+ bi = dev->block_info;
+ for (blk = dev->internal_start_block; blk <= dev->internal_end_block;
+ blk++) {
+ yaffs_clear_chunk_bits(dev, blk);
+ bi->pages_in_use = 0;
+ bi->soft_del_pages = 0;
+
+ yaffs_query_init_block_state(dev, blk, &state, &seq_number);
+
+ bi->block_state = state;
+ bi->seq_number = seq_number;
+
+ if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK)
+ bi->block_state = state = YAFFS_BLOCK_STATE_DEAD;
+
+ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG,
+ "Block scanning block %d state %d seq %d",
+ blk, state, seq_number);
+
+ if (state == YAFFS_BLOCK_STATE_DEAD) {
+ yaffs_trace(YAFFS_TRACE_BAD_BLOCKS,
+ "block %d is bad", blk);
+ } else if (state == YAFFS_BLOCK_STATE_EMPTY) {
+ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty ");
+ dev->n_erased_blocks++;
+ dev->n_free_chunks += dev->param.chunks_per_block;
+ }
+ bi++;
+ }
+
+ /* For each block.... */
+ for (blk = dev->internal_start_block;
+ !alloc_failed && blk <= dev->internal_end_block; blk++) {
+
+ cond_resched();
+
+ bi = yaffs_get_block_info(dev, blk);
+ state = bi->block_state;
+
+ deleted = 0;
+
+ /* For each chunk in each block that needs scanning.... */
+ for (c = 0;
+ !alloc_failed && c < dev->param.chunks_per_block &&
+ state == YAFFS_BLOCK_STATE_NEEDS_SCAN; c++) {
+ /* Read the tags and decide what to do */
+ chunk = blk * dev->param.chunks_per_block + c;
+
+ yaffs_rd_chunk_tags_nand(dev, chunk, NULL, &tags);
+
+ /* Let's have a good look at this chunk... */
+
+ if (tags.ecc_result == YAFFS_ECC_RESULT_UNFIXED ||
+ tags.is_deleted) {
+ /* YAFFS1 only...
+ * A deleted chunk
+ */
+ deleted++;
+ dev->n_free_chunks++;
+ } else if (!tags.chunk_used) {
+ /* An unassigned chunk in the block
+ * This means that either the block is empty or
+ * this is the one being allocated from
+ */
+
+ if (c == 0) {
+ /* We're looking at the first chunk in
+ *the block so the block is unused */
+ state = YAFFS_BLOCK_STATE_EMPTY;
+ dev->n_erased_blocks++;
+ } else {
+ /* this is the block being allocated */
+ yaffs_trace(YAFFS_TRACE_SCAN,
+ " Allocating from %d %d",
+ blk, c);
+ state = YAFFS_BLOCK_STATE_ALLOCATING;
+ dev->alloc_block = blk;
+ dev->alloc_page = c;
+ dev->alloc_block_finder = blk;
+
+ }
+
+ dev->n_free_chunks +=
+ (dev->param.chunks_per_block - c);
+ } else if (tags.chunk_id > 0) {
+ /* chunk_id > 0 so it is a data chunk... */
+ unsigned int endpos;
+
+ yaffs_set_chunk_bit(dev, blk, c);
+ bi->pages_in_use++;
+
+ in = yaffs_find_or_create_by_number(dev,
+ tags.obj_id,
+ YAFFS_OBJECT_TYPE_FILE);
+ /* PutChunkIntoFile checks for a clash
+ * (two data chunks with the same chunk_id).
+ */
+
+ if (!in)
+ alloc_failed = 1;
+
+ if (in) {
+ if (!yaffs_put_chunk_in_file
+ (in, tags.chunk_id, chunk, 1))
+ alloc_failed = 1;
+ }
+
+ endpos =
+ (tags.chunk_id - 1) *
+ dev->data_bytes_per_chunk +
+ tags.n_bytes;
+ if (in &&
+ in->variant_type ==
+ YAFFS_OBJECT_TYPE_FILE &&
+ in->variant.file_variant.scanned_size <
+ endpos) {
+ in->variant.file_variant.scanned_size =
+ endpos;
+ if (!dev->param.use_header_file_size) {
+ in->variant.
+ file_variant.file_size =
+ in->variant.
+ file_variant.scanned_size;
+ }
+
+ }
+ } else {
+ /* chunk_id == 0, so it is an ObjectHeader.
+ * Make the object
+ */
+ yaffs_set_chunk_bit(dev, blk, c);
+ bi->pages_in_use++;
+
+ yaffs_rd_chunk_tags_nand(dev, chunk,
+ chunk_data, NULL);
+
+ oh = (struct yaffs_obj_hdr *)chunk_data;
+
+ in = yaffs_find_by_number(dev, tags.obj_id);
+ if (in && in->variant_type != oh->type) {
+ /* This should not happen, but somehow
+ * Wev'e ended up with an obj_id that
+ * has been reused but not yet deleted,
+ * and worse still it has changed type.
+ * Delete the old object.
+ */
+
+ yaffs_del_obj(in);
+ in = NULL;
+ }
+
+ in = yaffs_find_or_create_by_number(dev,
+ tags.obj_id,
+ oh->type);
+
+ if (!in)
+ alloc_failed = 1;
+
+ if (in && oh->shadows_obj > 0) {
+
+ struct yaffs_shadow_fixer *fixer;
+ fixer =
+ kmalloc(sizeof
+ (struct yaffs_shadow_fixer),
+ GFP_NOFS);
+ if (fixer) {
+ fixer->next = shadow_fixers;
+ shadow_fixers = fixer;
+ fixer->obj_id = tags.obj_id;
+ fixer->shadowed_id =
+ oh->shadows_obj;
+ yaffs_trace(YAFFS_TRACE_SCAN,
+ " Shadow fixer: %d shadows %d",
+ fixer->obj_id,
+ fixer->shadowed_id);
+
+ }
+
+ }
+
+ if (in && in->valid) {
+ /* We have already filled this one.
+ * We have a duplicate and need to
+ * resolve it. */
+
+ unsigned existing_serial = in->serial;
+ unsigned new_serial =
+ tags.serial_number;
+
+ if (((existing_serial + 1) & 3) ==
+ new_serial) {
+ /* Use new one - destroy the
+ * exisiting one */
+ yaffs_chunk_del(dev,
+ in->hdr_chunk,
+ 1, __LINE__);
+ in->valid = 0;
+ } else {
+ /* Use existing - destroy
+ * this one. */
+ yaffs_chunk_del(dev, chunk, 1,
+ __LINE__);
+ }
+ }
+
+ if (in && !in->valid &&
+ (tags.obj_id == YAFFS_OBJECTID_ROOT ||
+ tags.obj_id ==
+ YAFFS_OBJECTID_LOSTNFOUND)) {
+ /* We only load some info, don't fiddle
+ * with directory structure */
+ in->valid = 1;
+ in->variant_type = oh->type;
+
+ in->yst_mode = oh->yst_mode;
+ yaffs_load_attribs(in, oh);
+ in->hdr_chunk = chunk;
+ in->serial = tags.serial_number;
+
+ } else if (in && !in->valid) {
+ /* we need to load this info */
+
+ in->valid = 1;
+ in->variant_type = oh->type;
+
+ in->yst_mode = oh->yst_mode;
+ yaffs_load_attribs(in, oh);
+ in->hdr_chunk = chunk;
+ in->serial = tags.serial_number;
+
+ yaffs_set_obj_name_from_oh(in, oh);
+ in->dirty = 0;
+
+ /* directory stuff...
+ * hook up to parent
+ */
+
+ parent =
+ yaffs_find_or_create_by_number
+ (dev, oh->parent_obj_id,
+ YAFFS_OBJECT_TYPE_DIRECTORY);
+ if (!parent)
+ alloc_failed = 1;
+ if (parent && parent->variant_type ==
+ YAFFS_OBJECT_TYPE_UNKNOWN) {
+ /* Set up as a directory */
+ parent->variant_type =
+ YAFFS_OBJECT_TYPE_DIRECTORY;
+ INIT_LIST_HEAD(&parent->
+ variant.dir_variant.
+ children);
+ } else if (!parent ||
+ parent->variant_type !=
+ YAFFS_OBJECT_TYPE_DIRECTORY) {
+ /* Hoosterman, a problem....
+ * We're trying to use a
+ * non-directory as a directory
+ */
+
+ yaffs_trace(YAFFS_TRACE_ERROR,
+ "yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found."
+ );
+ parent = dev->lost_n_found;
+ }
+
+ yaffs_add_obj_to_dir(parent, in);
+
+ switch (in->variant_type) {
+ case YAFFS_OBJECT_TYPE_UNKNOWN:
+ /* Todo got a problem */
+ break;
+ case YAFFS_OBJECT_TYPE_FILE:
+ if (dev->param.
+ use_header_file_size)
+ in->variant.
+ file_variant.file_size
+ = yaffs_oh_to_size(oh);
+ break;
+ case YAFFS_OBJECT_TYPE_HARDLINK:
+ in->variant.
+ hardlink_variant.equiv_id =
+ oh->equiv_id;
+ list_add(&in->hard_links,
+ &hard_list);
+ break;
+ case YAFFS_OBJECT_TYPE_DIRECTORY:
+ /* Do nothing */
+ break;
+ case YAFFS_OBJECT_TYPE_SPECIAL:
+ /* Do nothing */
+ break;
+ case YAFFS_OBJECT_TYPE_SYMLINK:
+ in->variant.symlink_variant.
+ alias =
+ yaffs_clone_str(oh->alias);
+ if (!in->variant.
+ symlink_variant.alias)
+ alloc_failed = 1;
+ break;
+ }
+ }
+ }
+ }
+
+ if (state == YAFFS_BLOCK_STATE_NEEDS_SCAN) {
+ /* If we got this far while scanning,
+ * then the block is fully allocated. */
+ state = YAFFS_BLOCK_STATE_FULL;
+ }
+
+ if (state == YAFFS_BLOCK_STATE_ALLOCATING) {
+ /* If the block was partially allocated then
+ * treat it as fully allocated. */
+ state = YAFFS_BLOCK_STATE_FULL;
+ dev->alloc_block = -1;
+ }
+
+ bi->block_state = state;
+
+ /* Now let's see if it was dirty */
+ if (bi->pages_in_use == 0 &&
+ !bi->has_shrink_hdr &&
+ bi->block_state == YAFFS_BLOCK_STATE_FULL)
+ yaffs_block_became_dirty(dev, blk);
+ }
+
+ /* Ok, we've done all the scanning.
+ * Fix up the hard link chains.
+ * We should now have scanned all the objects, now it's time to add
+ * these hardlinks.
+ */
+
+ yaffs_link_fixup(dev, &hard_list);
+
+ /*
+ * Fix up any shadowed objects.
+ * There should not be more than one of these.
+ */
+ {
+ struct yaffs_shadow_fixer *fixer;
+ struct yaffs_obj *obj;
+
+ while (shadow_fixers) {
+ fixer = shadow_fixers;
+ shadow_fixers = fixer->next;
+ /* Complete the rename transaction by deleting the
+ * shadowed object then setting the object header
+ to unshadowed.
+ */
+ obj = yaffs_find_by_number(dev, fixer->shadowed_id);
+ if (obj)
+ yaffs_del_obj(obj);
+
+ obj = yaffs_find_by_number(dev, fixer->obj_id);
+
+ if (obj)
+ yaffs_update_oh(obj, NULL, 1, 0, 0, NULL);
+
+ kfree(fixer);
+ }
+ }
+
+ yaffs_release_temp_buffer(dev, chunk_data);
+
+ if (alloc_failed)
+ return YAFFS_FAIL;
+
+ yaffs_trace(YAFFS_TRACE_SCAN, "yaffs1_scan ends");
+
+ return YAFFS_OK;
+}
diff --git a/roms/u-boot/fs/yaffs2/yaffs_yaffs1.h b/roms/u-boot/fs/yaffs2/yaffs_yaffs1.h
new file mode 100644
index 000000000..97e2fdd08
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_yaffs1.h
@@ -0,0 +1,22 @@
+/*
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ */
+
+#ifndef __YAFFS_YAFFS1_H__
+#define __YAFFS_YAFFS1_H__
+
+#include "yaffs_guts.h"
+int yaffs1_scan(struct yaffs_dev *dev);
+
+#endif
diff --git a/roms/u-boot/fs/yaffs2/yaffs_yaffs2.c b/roms/u-boot/fs/yaffs2/yaffs_yaffs2.c
new file mode 100644
index 000000000..14d497eb9
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_yaffs2.c
@@ -0,0 +1,1527 @@
+/*
+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "yaffs_guts.h"
+#include "yaffs_trace.h"
+#include "yaffs_yaffs2.h"
+#include "yaffs_checkptrw.h"
+#include "yaffs_bitmap.h"
+#include "yaffs_nand.h"
+#include "yaffs_getblockinfo.h"
+#include "yaffs_verify.h"
+#include "yaffs_attribs.h"
+#include "yaffs_summary.h"
+#include <dm/devres.h>
+
+/*
+ * Checkpoints are really no benefit on very small partitions.
+ *
+ * To save space on small partitions don't bother with checkpoints unless
+ * the partition is at least this big.
+ */
+#define YAFFS_CHECKPOINT_MIN_BLOCKS 60
+#define YAFFS_SMALL_HOLE_THRESHOLD 4
+
+/*
+ * Oldest Dirty Sequence Number handling.
+ */
+
+/* yaffs_calc_oldest_dirty_seq()
+ * yaffs2_find_oldest_dirty_seq()
+ * Calculate the oldest dirty sequence number if we don't know it.
+ */
+void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev)
+{
+ int i;
+ unsigned seq;
+ unsigned block_no = 0;
+ struct yaffs_block_info *b;
+
+ if (!dev->param.is_yaffs2)
+ return;
+
+ /* Find the oldest dirty sequence number. */
+ seq = dev->seq_number + 1;
+ b = dev->block_info;
+ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) {
+ if (b->block_state == YAFFS_BLOCK_STATE_FULL &&
+ (b->pages_in_use - b->soft_del_pages) <
+ dev->param.chunks_per_block &&
+ b->seq_number < seq) {
+ seq = b->seq_number;
+ block_no = i;
+ }
+ b++;
+ }
+
+ if (block_no) {
+ dev->oldest_dirty_seq = seq;
+ dev->oldest_dirty_block = block_no;
+ }
+}
+
+void yaffs2_find_oldest_dirty_seq(struct yaffs_dev *dev)
+{
+ if (!dev->param.is_yaffs2)
+ return;
+
+ if (!dev->oldest_dirty_seq)
+ yaffs_calc_oldest_dirty_seq(dev);
+}
+
+/*
+ * yaffs_clear_oldest_dirty_seq()
+ * Called when a block is erased or marked bad. (ie. when its seq_number
+ * becomes invalid). If the value matches the oldest then we clear
+ * dev->oldest_dirty_seq to force its recomputation.
+ */
+void yaffs2_clear_oldest_dirty_seq(struct yaffs_dev *dev,
+ struct yaffs_block_info *bi)
+{
+
+ if (!dev->param.is_yaffs2)
+ return;
+
+ if (!bi || bi->seq_number == dev->oldest_dirty_seq) {
+ dev->oldest_dirty_seq = 0;
+ dev->oldest_dirty_block = 0;
+ }
+}
+
+/*
+ * yaffs2_update_oldest_dirty_seq()
+ * Update the oldest dirty sequence number whenever we dirty a block.
+ * Only do this if the oldest_dirty_seq is actually being tracked.
+ */
+void yaffs2_update_oldest_dirty_seq(struct yaffs_dev *dev, unsigned block_no,
+ struct yaffs_block_info *bi)
+{
+ if (!dev->param.is_yaffs2)
+ return;
+
+ if (dev->oldest_dirty_seq) {
+ if (dev->oldest_dirty_seq > bi->seq_number) {
+ dev->oldest_dirty_seq = bi->seq_number;
+ dev->oldest_dirty_block = block_no;
+ }
+ }
+}
+
+int yaffs_block_ok_for_gc(struct yaffs_dev *dev, struct yaffs_block_info *bi)
+{
+
+ if (!dev->param.is_yaffs2)
+ return 1; /* disqualification only applies to yaffs2. */
+
+ if (!bi->has_shrink_hdr)
+ return 1; /* can gc */
+
+ yaffs2_find_oldest_dirty_seq(dev);
+
+ /* Can't do gc of this block if there are any blocks older than this
+ * one that have discarded pages.
+ */
+ return (bi->seq_number <= dev->oldest_dirty_seq);
+}
+
+/*
+ * yaffs2_find_refresh_block()
+ * periodically finds the oldest full block by sequence number for refreshing.
+ * Only for yaffs2.
+ */
+u32 yaffs2_find_refresh_block(struct yaffs_dev *dev)
+{
+ u32 b;
+ u32 oldest = 0;
+ u32 oldest_seq = 0;
+ struct yaffs_block_info *bi;
+
+ if (!dev->param.is_yaffs2)
+ return oldest;
+
+ /*
+ * If refresh period < 10 then refreshing is disabled.
+ */
+ if (dev->param.refresh_period < 10)
+ return oldest;
+
+ /*
+ * Fix broken values.
+ */
+ if (dev->refresh_skip > dev->param.refresh_period)
+ dev->refresh_skip = dev->param.refresh_period;
+
+ if (dev->refresh_skip > 0)
+ return oldest;
+
+ /*
+ * Refresh skip is now zero.
+ * We'll do a refresh this time around....
+ * Update the refresh skip and find the oldest block.
+ */
+ dev->refresh_skip = dev->param.refresh_period;
+ dev->refresh_count++;
+ bi = dev->block_info;
+ for (b = dev->internal_start_block; b <= dev->internal_end_block; b++) {
+
+ if (bi->block_state == YAFFS_BLOCK_STATE_FULL) {
+
+ if (oldest < 1 || bi->seq_number < oldest_seq) {
+ oldest = b;
+ oldest_seq = bi->seq_number;
+ }
+ }
+ bi++;
+ }
+
+ if (oldest > 0) {
+ yaffs_trace(YAFFS_TRACE_GC,
+ "GC refresh count %d selected block %d with seq_number %d",
+ dev->refresh_count, oldest, oldest_seq);
+ }
+
+ return oldest;
+}
+
+int yaffs2_checkpt_required(struct yaffs_dev *dev)
+{
+ int nblocks;
+
+ if (!dev->param.is_yaffs2)
+ return 0;
+
+ nblocks = dev->internal_end_block - dev->internal_start_block + 1;
+
+ return !dev->param.skip_checkpt_wr &&
+ !dev->read_only && (nblocks >= YAFFS_CHECKPOINT_MIN_BLOCKS);
+}
+
+int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev)
+{
+ int retval;
+ int n_bytes = 0;
+ int n_blocks;
+ int dev_blocks;
+
+ if (!dev->param.is_yaffs2)
+ return 0;
+
+ if (!dev->checkpoint_blocks_required && yaffs2_checkpt_required(dev)) {
+ /* Not a valid value so recalculate */
+ dev_blocks = dev->param.end_block - dev->param.start_block + 1;
+ n_bytes += sizeof(struct yaffs_checkpt_validity);
+ n_bytes += sizeof(struct yaffs_checkpt_dev);
+ n_bytes += dev_blocks * sizeof(struct yaffs_block_info);
+ n_bytes += dev_blocks * dev->chunk_bit_stride;
+ n_bytes +=
+ (sizeof(struct yaffs_checkpt_obj) + sizeof(u32)) *
+ dev->n_obj;
+ n_bytes += (dev->tnode_size + sizeof(u32)) * dev->n_tnodes;
+ n_bytes += sizeof(struct yaffs_checkpt_validity);
+ n_bytes += sizeof(u32); /* checksum */
+
+ /* Round up and add 2 blocks to allow for some bad blocks,
+ * so add 3 */
+
+ n_blocks =
+ (n_bytes /
+ (dev->data_bytes_per_chunk *
+ dev->param.chunks_per_block)) + 3;
+
+ dev->checkpoint_blocks_required = n_blocks;
+ }
+
+ retval = dev->checkpoint_blocks_required - dev->blocks_in_checkpt;
+ if (retval < 0)
+ retval = 0;
+ return retval;
+}
+
+/*--------------------- Checkpointing --------------------*/
+
+static int yaffs2_wr_checkpt_validity_marker(struct yaffs_dev *dev, int head)
+{
+ struct yaffs_checkpt_validity cp;
+
+ memset(&cp, 0, sizeof(cp));
+
+ cp.struct_type = sizeof(cp);
+ cp.magic = YAFFS_MAGIC;
+ cp.version = YAFFS_CHECKPOINT_VERSION;
+ cp.head = (head) ? 1 : 0;
+
+ return (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)) ? 1 : 0;
+}
+
+static int yaffs2_rd_checkpt_validity_marker(struct yaffs_dev *dev, int head)
+{
+ struct yaffs_checkpt_validity cp;
+ int ok;
+
+ ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp));
+
+ if (ok)
+ ok = (cp.struct_type == sizeof(cp)) &&
+ (cp.magic == YAFFS_MAGIC) &&
+ (cp.version == YAFFS_CHECKPOINT_VERSION) &&
+ (cp.head == ((head) ? 1 : 0));
+ return ok ? 1 : 0;
+}
+
+static void yaffs2_dev_to_checkpt_dev(struct yaffs_checkpt_dev *cp,
+ struct yaffs_dev *dev)
+{
+ cp->n_erased_blocks = dev->n_erased_blocks;
+ cp->alloc_block = dev->alloc_block;
+ cp->alloc_page = dev->alloc_page;
+ cp->n_free_chunks = dev->n_free_chunks;
+
+ cp->n_deleted_files = dev->n_deleted_files;
+ cp->n_unlinked_files = dev->n_unlinked_files;
+ cp->n_bg_deletions = dev->n_bg_deletions;
+ cp->seq_number = dev->seq_number;
+
+}
+
+static void yaffs_checkpt_dev_to_dev(struct yaffs_dev *dev,
+ struct yaffs_checkpt_dev *cp)
+{
+ dev->n_erased_blocks = cp->n_erased_blocks;
+ dev->alloc_block = cp->alloc_block;
+ dev->alloc_page = cp->alloc_page;
+ dev->n_free_chunks = cp->n_free_chunks;
+
+ dev->n_deleted_files = cp->n_deleted_files;
+ dev->n_unlinked_files = cp->n_unlinked_files;
+ dev->n_bg_deletions = cp->n_bg_deletions;
+ dev->seq_number = cp->seq_number;
+}
+
+static int yaffs2_wr_checkpt_dev(struct yaffs_dev *dev)
+{
+ struct yaffs_checkpt_dev cp;
+ u32 n_bytes;
+ u32 n_blocks = dev->internal_end_block - dev->internal_start_block + 1;
+ int ok;
+
+ /* Write device runtime values */
+ yaffs2_dev_to_checkpt_dev(&cp, dev);
+ cp.struct_type = sizeof(cp);
+
+ ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp));
+ if (!ok)
+ return 0;
+
+ /* Write block info */
+ n_bytes = n_blocks * sizeof(struct yaffs_block_info);
+ ok = (yaffs2_checkpt_wr(dev, dev->block_info, n_bytes) == n_bytes);
+ if (!ok)
+ return 0;
+
+ /* Write chunk bits */
+ n_bytes = n_blocks * dev->chunk_bit_stride;
+ ok = (yaffs2_checkpt_wr(dev, dev->chunk_bits, n_bytes) == n_bytes);
+
+ return ok ? 1 : 0;
+}
+
+static int yaffs2_rd_checkpt_dev(struct yaffs_dev *dev)
+{
+ struct yaffs_checkpt_dev cp;
+ u32 n_bytes;
+ u32 n_blocks =
+ (dev->internal_end_block - dev->internal_start_block + 1);
+ int ok;
+
+ ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp));
+ if (!ok)
+ return 0;
+
+ if (cp.struct_type != sizeof(cp))
+ return 0;
+
+ yaffs_checkpt_dev_to_dev(dev, &cp);
+
+ n_bytes = n_blocks * sizeof(struct yaffs_block_info);
+
+ ok = (yaffs2_checkpt_rd(dev, dev->block_info, n_bytes) == n_bytes);
+
+ if (!ok)
+ return 0;
+
+ n_bytes = n_blocks * dev->chunk_bit_stride;
+
+ ok = (yaffs2_checkpt_rd(dev, dev->chunk_bits, n_bytes) == n_bytes);
+
+ return ok ? 1 : 0;
+}
+
+static void yaffs2_obj_checkpt_obj(struct yaffs_checkpt_obj *cp,
+ struct yaffs_obj *obj)
+{
+ cp->obj_id = obj->obj_id;
+ cp->parent_id = (obj->parent) ? obj->parent->obj_id : 0;
+ cp->hdr_chunk = obj->hdr_chunk;
+ cp->variant_type = obj->variant_type;
+ cp->deleted = obj->deleted;
+ cp->soft_del = obj->soft_del;
+ cp->unlinked = obj->unlinked;
+ cp->fake = obj->fake;
+ cp->rename_allowed = obj->rename_allowed;
+ cp->unlink_allowed = obj->unlink_allowed;
+ cp->serial = obj->serial;
+ cp->n_data_chunks = obj->n_data_chunks;
+
+ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE)
+ cp->size_or_equiv_obj = obj->variant.file_variant.file_size;
+ else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK)
+ cp->size_or_equiv_obj = obj->variant.hardlink_variant.equiv_id;
+}
+
+static int yaffs2_checkpt_obj_to_obj(struct yaffs_obj *obj,
+ struct yaffs_checkpt_obj *cp)
+{
+ struct yaffs_obj *parent;
+
+ if (obj->variant_type != cp->variant_type) {
+ yaffs_trace(YAFFS_TRACE_ERROR,
+ "Checkpoint read object %d type %d chunk %d does not match existing object type %d",
+ cp->obj_id, cp->variant_type, cp->hdr_chunk,
+ obj->variant_type);
+ return 0;
+ }
+
+ obj->obj_id = cp->obj_id;
+
+ if (cp->parent_id)
+ parent = yaffs_find_or_create_by_number(obj->my_dev,
+ cp->parent_id,
+ YAFFS_OBJECT_TYPE_DIRECTORY);
+ else
+ parent = NULL;
+
+ if (parent) {
+ if (parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {
+ yaffs_trace(YAFFS_TRACE_ALWAYS,
+ "Checkpoint read object %d parent %d type %d chunk %d Parent type, %d, not directory",
+ cp->obj_id, cp->parent_id,
+ cp->variant_type, cp->hdr_chunk,
+ parent->variant_type);
+ return 0;
+ }
+ yaffs_add_obj_to_dir(parent, obj);
+ }
+
+ obj->hdr_chunk = cp->hdr_chunk;
+ obj->variant_type = cp->variant_type;
+ obj->deleted = cp->deleted;
+ obj->soft_del = cp->soft_del;
+ obj->unlinked = cp->unlinked;
+ obj->fake = cp->fake;
+ obj->rename_allowed = cp->rename_allowed;
+ obj->unlink_allowed = cp->unlink_allowed;
+ obj->serial = cp->serial;
+ obj->n_data_chunks = cp->n_data_chunks;
+
+ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE)
+ obj->variant.file_variant.file_size = cp->size_or_equiv_obj;
+ else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK)
+ obj->variant.hardlink_variant.equiv_id = cp->size_or_equiv_obj;
+
+ if (obj->hdr_chunk > 0)
+ obj->lazy_loaded = 1;
+ return 1;
+}
+
+static int yaffs2_checkpt_tnode_worker(struct yaffs_obj *in,
+ struct yaffs_tnode *tn, u32 level,
+ int chunk_offset)
+{
+ int i;
+ struct yaffs_dev *dev = in->my_dev;
+ int ok = 1;
+ u32 base_offset;
+
+ if (!tn)
+ return 1;
+
+ if (level > 0) {
+ for (i = 0; i < YAFFS_NTNODES_INTERNAL && ok; i++) {
+ if (!tn->internal[i])
+ continue;
+ ok = yaffs2_checkpt_tnode_worker(in,
+ tn->internal[i],
+ level - 1,
+ (chunk_offset <<
+ YAFFS_TNODES_INTERNAL_BITS) + i);
+ }
+ return ok;
+ }
+
+ /* Level 0 tnode */
+ base_offset = chunk_offset << YAFFS_TNODES_LEVEL0_BITS;
+ ok = (yaffs2_checkpt_wr(dev, &base_offset, sizeof(base_offset)) ==
+ sizeof(base_offset));
+ if (ok)
+ ok = (yaffs2_checkpt_wr(dev, tn, dev->tnode_size) ==
+ dev->tnode_size);
+
+ return ok;
+}
+
+static int yaffs2_wr_checkpt_tnodes(struct yaffs_obj *obj)
+{
+ u32 end_marker = ~0;
+ int ok = 1;
+
+ if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE)
+ return ok;
+
+ ok = yaffs2_checkpt_tnode_worker(obj,
+ obj->variant.file_variant.top,
+ obj->variant.file_variant.
+ top_level, 0);
+ if (ok)
+ ok = (yaffs2_checkpt_wr(obj->my_dev, &end_marker,
+ sizeof(end_marker)) == sizeof(end_marker));
+
+ return ok ? 1 : 0;
+}
+
+static int yaffs2_rd_checkpt_tnodes(struct yaffs_obj *obj)
+{
+ u32 base_chunk;
+ int ok = 1;
+ struct yaffs_dev *dev = obj->my_dev;
+ struct yaffs_file_var *file_stuct_ptr = &obj->variant.file_variant;
+ struct yaffs_tnode *tn;
+ int nread = 0;
+
+ ok = (yaffs2_checkpt_rd(dev, &base_chunk, sizeof(base_chunk)) ==
+ sizeof(base_chunk));
+
+ while (ok && (~base_chunk)) {
+ nread++;
+ /* Read level 0 tnode */
+
+ tn = yaffs_get_tnode(dev);
+ if (tn)
+ ok = (yaffs2_checkpt_rd(dev, tn, dev->tnode_size) ==
+ dev->tnode_size);
+ else
+ ok = 0;
+
+ if (tn && ok)
+ ok = yaffs_add_find_tnode_0(dev,
+ file_stuct_ptr,
+ base_chunk, tn) ? 1 : 0;
+
+ if (ok)
+ ok = (yaffs2_checkpt_rd
+ (dev, &base_chunk,
+ sizeof(base_chunk)) == sizeof(base_chunk));
+ }
+
+ yaffs_trace(YAFFS_TRACE_CHECKPOINT,
+ "Checkpoint read tnodes %d records, last %d. ok %d",
+ nread, base_chunk, ok);
+
+ return ok ? 1 : 0;
+}
+
+static int yaffs2_wr_checkpt_objs(struct yaffs_dev *dev)
+{
+ struct yaffs_obj *obj;
+ struct yaffs_checkpt_obj cp;
+ int i;
+ int ok = 1;
+ struct list_head *lh;
+
+ /* Iterate through the objects in each hash entry,
+ * dumping them to the checkpointing stream.
+ */
+
+ for (i = 0; ok && i < YAFFS_NOBJECT_BUCKETS; i++) {
+ list_for_each(lh, &dev->obj_bucket[i].list) {
+ obj = list_entry(lh, struct yaffs_obj, hash_link);
+ if (!obj->defered_free) {
+ yaffs2_obj_checkpt_obj(&cp, obj);
+ cp.struct_type = sizeof(cp);
+
+ yaffs_trace(YAFFS_TRACE_CHECKPOINT,
+ "Checkpoint write object %d parent %d type %d chunk %d obj addr %p",
+ cp.obj_id, cp.parent_id,
+ cp.variant_type, cp.hdr_chunk, obj);
+
+ ok = (yaffs2_checkpt_wr(dev, &cp,
+ sizeof(cp)) == sizeof(cp));
+
+ if (ok &&
+ obj->variant_type ==
+ YAFFS_OBJECT_TYPE_FILE)
+ ok = yaffs2_wr_checkpt_tnodes(obj);
+ }
+ }
+ }
+
+ /* Dump end of list */
+ memset(&cp, 0xff, sizeof(struct yaffs_checkpt_obj));
+ cp.struct_type = sizeof(cp);
+
+ if (ok)
+ ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp));
+
+ return ok ? 1 : 0;
+}
+
+static int yaffs2_rd_checkpt_objs(struct yaffs_dev *dev)
+{
+ struct yaffs_obj *obj;
+ struct yaffs_checkpt_obj cp;
+ int ok = 1;
+ int done = 0;
+ LIST_HEAD(hard_list);
+
+
+ while (ok && !done) {
+ ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp));
+ if (cp.struct_type != sizeof(cp)) {
+ yaffs_trace(YAFFS_TRACE_CHECKPOINT,
+ "struct size %d instead of %d ok %d",
+ cp.struct_type, (int)sizeof(cp), ok);
+ ok = 0;
+ }
+
+ yaffs_trace(YAFFS_TRACE_CHECKPOINT,
+ "Checkpoint read object %d parent %d type %d chunk %d ",
+ cp.obj_id, cp.parent_id, cp.variant_type,
+ cp.hdr_chunk);
+
+ if (ok && cp.obj_id == ~0) {
+ done = 1;
+ } else if (ok) {
+ obj =
+ yaffs_find_or_create_by_number(dev, cp.obj_id,
+ cp.variant_type);
+ if (obj) {
+ ok = yaffs2_checkpt_obj_to_obj(obj, &cp);
+ if (!ok)
+ break;
+ if (obj->variant_type ==
+ YAFFS_OBJECT_TYPE_FILE) {
+ ok = yaffs2_rd_checkpt_tnodes(obj);
+ } else if (obj->variant_type ==
+ YAFFS_OBJECT_TYPE_HARDLINK) {
+ list_add(&obj->hard_links, &hard_list);
+ }
+ } else {
+ ok = 0;
+ }
+ }
+ }
+
+ if (ok)
+ yaffs_link_fixup(dev, &hard_list);
+
+ return ok ? 1 : 0;
+}
+
+static int yaffs2_wr_checkpt_sum(struct yaffs_dev *dev)
+{
+ u32 checkpt_sum;
+ int ok;
+
+ yaffs2_get_checkpt_sum(dev, &checkpt_sum);
+
+ ok = (yaffs2_checkpt_wr(dev, &checkpt_sum, sizeof(checkpt_sum)) ==
+ sizeof(checkpt_sum));
+
+ if (!ok)
+ return 0;
+
+ return 1;
+}
+
+static int yaffs2_rd_checkpt_sum(struct yaffs_dev *dev)
+{
+ u32 checkpt_sum0;
+ u32 checkpt_sum1;
+ int ok;
+
+ yaffs2_get_checkpt_sum(dev, &checkpt_sum0);
+
+ ok = (yaffs2_checkpt_rd(dev, &checkpt_sum1, sizeof(checkpt_sum1)) ==
+ sizeof(checkpt_sum1));
+
+ if (!ok)
+ return 0;
+
+ if (checkpt_sum0 != checkpt_sum1)
+ return 0;
+
+ return 1;
+}
+
+static int yaffs2_wr_checkpt_data(struct yaffs_dev *dev)
+{
+ int ok = 1;
+
+ if (!yaffs2_checkpt_required(dev)) {
+ yaffs_trace(YAFFS_TRACE_CHECKPOINT,
+ "skipping checkpoint write");
+ ok = 0;
+ }
+
+ if (ok)
+ ok = yaffs2_checkpt_open(dev, 1);
+
+ if (ok) {
+ yaffs_trace(YAFFS_TRACE_CHECKPOINT,
+ "write checkpoint validity");
+ ok = yaffs2_wr_checkpt_validity_marker(dev, 1);
+ }
+ if (ok) {
+ yaffs_trace(YAFFS_TRACE_CHECKPOINT,
+ "write checkpoint device");
+ ok = yaffs2_wr_checkpt_dev(dev);
+ }
+ if (ok) {
+ yaffs_trace(YAFFS_TRACE_CHECKPOINT,
+ "write checkpoint objects");
+ ok = yaffs2_wr_checkpt_objs(dev);
+ }
+ if (ok) {
+ yaffs_trace(YAFFS_TRACE_CHECKPOINT,
+ "write checkpoint validity");
+ ok = yaffs2_wr_checkpt_validity_marker(dev, 0);
+ }
+
+ if (ok)
+ ok = yaffs2_wr_checkpt_sum(dev);
+
+ if (!yaffs_checkpt_close(dev))
+ ok = 0;
+
+ if (ok)
+ dev->is_checkpointed = 1;
+ else
+ dev->is_checkpointed = 0;
+
+ return dev->is_checkpointed;
+}
+
+static int yaffs2_rd_checkpt_data(struct yaffs_dev *dev)
+{
+ int ok = 1;
+
+ if (!dev->param.is_yaffs2)
+ ok = 0;
+
+ if (ok && dev->param.skip_checkpt_rd) {
+ yaffs_trace(YAFFS_TRACE_CHECKPOINT,
+ "skipping checkpoint read");
+ ok = 0;
+ }
+
+ if (ok)
+ ok = yaffs2_checkpt_open(dev, 0); /* open for read */
+
+ if (ok) {
+ yaffs_trace(YAFFS_TRACE_CHECKPOINT,
+ "read checkpoint validity");
+ ok = yaffs2_rd_checkpt_validity_marker(dev, 1);
+ }
+ if (ok) {
+ yaffs_trace(YAFFS_TRACE_CHECKPOINT,
+ "read checkpoint device");
+ ok = yaffs2_rd_checkpt_dev(dev);
+ }
+ if (ok) {
+ yaffs_trace(YAFFS_TRACE_CHECKPOINT,
+ "read checkpoint objects");
+ ok = yaffs2_rd_checkpt_objs(dev);
+ }
+ if (ok) {
+ yaffs_trace(YAFFS_TRACE_CHECKPOINT,
+ "read checkpoint validity");
+ ok = yaffs2_rd_checkpt_validity_marker(dev, 0);
+ }
+
+ if (ok) {
+ ok = yaffs2_rd_checkpt_sum(dev);
+ yaffs_trace(YAFFS_TRACE_CHECKPOINT,
+ "read checkpoint checksum %d", ok);
+ }
+
+ if (!yaffs_checkpt_close(dev))
+ ok = 0;
+
+ if (ok)
+ dev->is_checkpointed = 1;
+ else
+ dev->is_checkpointed = 0;
+
+ return ok ? 1 : 0;
+}
+
+void yaffs2_checkpt_invalidate(struct yaffs_dev *dev)
+{
+ if (dev->is_checkpointed || dev->blocks_in_checkpt > 0) {
+ dev->is_checkpointed = 0;
+ yaffs2_checkpt_invalidate_stream(dev);
+ }
+ if (dev->param.sb_dirty_fn)
+ dev->param.sb_dirty_fn(dev);
+}
+
+int yaffs_checkpoint_save(struct yaffs_dev *dev)
+{
+ yaffs_trace(YAFFS_TRACE_CHECKPOINT,
+ "save entry: is_checkpointed %d",
+ dev->is_checkpointed);
+
+ yaffs_verify_objects(dev);
+ yaffs_verify_blocks(dev);
+ yaffs_verify_free_chunks(dev);
+
+ if (!dev->is_checkpointed) {
+ yaffs2_checkpt_invalidate(dev);
+ yaffs2_wr_checkpt_data(dev);
+ }
+
+ yaffs_trace(YAFFS_TRACE_CHECKPOINT | YAFFS_TRACE_MOUNT,
+ "save exit: is_checkpointed %d",
+ dev->is_checkpointed);
+
+ return dev->is_checkpointed;
+}
+
+int yaffs2_checkpt_restore(struct yaffs_dev *dev)
+{
+ int retval;
+
+ yaffs_trace(YAFFS_TRACE_CHECKPOINT,
+ "restore entry: is_checkpointed %d",
+ dev->is_checkpointed);
+
+ retval = yaffs2_rd_checkpt_data(dev);
+
+ if (dev->is_checkpointed) {
+ yaffs_verify_objects(dev);
+ yaffs_verify_blocks(dev);
+ yaffs_verify_free_chunks(dev);
+ }
+
+ yaffs_trace(YAFFS_TRACE_CHECKPOINT,
+ "restore exit: is_checkpointed %d",
+ dev->is_checkpointed);
+
+ return retval;
+}
+
+int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size)
+{
+ /* if new_size > old_file_size.
+ * We're going to be writing a hole.
+ * If the hole is small then write zeros otherwise write a start
+ * of hole marker.
+ */
+ loff_t old_file_size;
+ loff_t increase;
+ int small_hole;
+ int result = YAFFS_OK;
+ struct yaffs_dev *dev = NULL;
+ u8 *local_buffer = NULL;
+ int small_increase_ok = 0;
+
+ if (!obj)
+ return YAFFS_FAIL;
+
+ if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE)
+ return YAFFS_FAIL;
+
+ dev = obj->my_dev;
+
+ /* Bail out if not yaffs2 mode */
+ if (!dev->param.is_yaffs2)
+ return YAFFS_OK;
+
+ old_file_size = obj->variant.file_variant.file_size;
+
+ if (new_size <= old_file_size)
+ return YAFFS_OK;
+
+ increase = new_size - old_file_size;
+
+ if (increase < YAFFS_SMALL_HOLE_THRESHOLD * dev->data_bytes_per_chunk &&
+ yaffs_check_alloc_available(dev, YAFFS_SMALL_HOLE_THRESHOLD + 1))
+ small_hole = 1;
+ else
+ small_hole = 0;
+
+ if (small_hole)
+ local_buffer = yaffs_get_temp_buffer(dev);
+
+ if (local_buffer) {
+ /* fill hole with zero bytes */
+ loff_t pos = old_file_size;
+ int this_write;
+ int written;
+ memset(local_buffer, 0, dev->data_bytes_per_chunk);
+ small_increase_ok = 1;
+
+ while (increase > 0 && small_increase_ok) {
+ this_write = increase;
+ if (this_write > dev->data_bytes_per_chunk)
+ this_write = dev->data_bytes_per_chunk;
+ written =
+ yaffs_do_file_wr(obj, local_buffer, pos, this_write,
+ 0);
+ if (written == this_write) {
+ pos += this_write;
+ increase -= this_write;
+ } else {
+ small_increase_ok = 0;
+ }
+ }
+
+ yaffs_release_temp_buffer(dev, local_buffer);
+
+ /* If out of space then reverse any chunks we've added */
+ if (!small_increase_ok)
+ yaffs_resize_file_down(obj, old_file_size);
+ }
+
+ if (!small_increase_ok &&
+ obj->parent &&
+ obj->parent->obj_id != YAFFS_OBJECTID_UNLINKED &&
+ obj->parent->obj_id != YAFFS_OBJECTID_DELETED) {
+ /* Write a hole start header with the old file size */
+ yaffs_update_oh(obj, NULL, 0, 1, 0, NULL);
+ }
+
+ return result;
+}
+
+struct yaffs_block_index {
+ int seq;
+ int block;
+};
+
+static int yaffs2_ybicmp(const void *a, const void *b)
+{
+ int aseq = ((struct yaffs_block_index *)a)->seq;
+ int bseq = ((struct yaffs_block_index *)b)->seq;
+ int ablock = ((struct yaffs_block_index *)a)->block;
+ int bblock = ((struct yaffs_block_index *)b)->block;
+
+ if (aseq == bseq)
+ return ablock - bblock;
+
+ return aseq - bseq;
+}
+
+static inline int yaffs2_scan_chunk(struct yaffs_dev *dev,
+ struct yaffs_block_info *bi,
+ int blk, int chunk_in_block,
+ int *found_chunks,
+ u8 *chunk_data,
+ struct list_head *hard_list,
+ int summary_available)
+{
+ struct yaffs_obj_hdr *oh;
+ struct yaffs_obj *in;
+ struct yaffs_obj *parent;
+ int equiv_id;
+ loff_t file_size;
+ int is_shrink;
+ int is_unlinked;
+ struct yaffs_ext_tags tags;
+ int alloc_failed = 0;
+ int chunk = blk * dev->param.chunks_per_block + chunk_in_block;
+ struct yaffs_file_var *file_var;
+ struct yaffs_hardlink_var *hl_var;
+ struct yaffs_symlink_var *sl_var;
+
+ if (summary_available) {
+ yaffs_summary_fetch(dev, &tags, chunk_in_block);
+ tags.seq_number = bi->seq_number;
+ }
+
+ if (!summary_available || tags.obj_id == 0) {
+ yaffs_rd_chunk_tags_nand(dev, chunk, NULL, &tags);
+ dev->tags_used++;
+ } else {
+ dev->summary_used++;
+ }
+
+ /* Let's have a good look at this chunk... */
+
+ if (!tags.chunk_used) {
+ /* An unassigned chunk in the block.
+ * If there are used chunks after this one, then
+ * it is a chunk that was skipped due to failing
+ * the erased check. Just skip it so that it can
+ * be deleted.
+ * But, more typically, We get here when this is
+ * an unallocated chunk and his means that
+ * either the block is empty or this is the one
+ * being allocated from
+ */
+
+ if (*found_chunks) {
+ /* This is a chunk that was skipped due
+ * to failing the erased check */
+ } else if (chunk_in_block == 0) {
+ /* We're looking at the first chunk in
+ * the block so the block is unused */
+ bi->block_state = YAFFS_BLOCK_STATE_EMPTY;
+ dev->n_erased_blocks++;
+ } else {
+ if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN ||
+ bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) {
+ if (dev->seq_number == bi->seq_number) {
+ /* Allocating from this block*/
+ yaffs_trace(YAFFS_TRACE_SCAN,
+ " Allocating from %d %d",
+ blk, chunk_in_block);
+
+ bi->block_state =
+ YAFFS_BLOCK_STATE_ALLOCATING;
+ dev->alloc_block = blk;
+ dev->alloc_page = chunk_in_block;
+ dev->alloc_block_finder = blk;
+ } else {
+ /* This is a partially written block
+ * that is not the current
+ * allocation block.
+ */
+ yaffs_trace(YAFFS_TRACE_SCAN,
+ "Partially written block %d detected. gc will fix this.",
+ blk);
+ }
+ }
+ }
+
+ dev->n_free_chunks++;
+
+ } else if (tags.ecc_result ==
+ YAFFS_ECC_RESULT_UNFIXED) {
+ yaffs_trace(YAFFS_TRACE_SCAN,
+ " Unfixed ECC in chunk(%d:%d), chunk ignored",
+ blk, chunk_in_block);
+ dev->n_free_chunks++;
+ } else if (tags.obj_id > YAFFS_MAX_OBJECT_ID ||
+ tags.chunk_id > YAFFS_MAX_CHUNK_ID ||
+ tags.obj_id == YAFFS_OBJECTID_SUMMARY ||
+ (tags.chunk_id > 0 &&
+ tags.n_bytes > dev->data_bytes_per_chunk) ||
+ tags.seq_number != bi->seq_number) {
+ yaffs_trace(YAFFS_TRACE_SCAN,
+ "Chunk (%d:%d) with bad tags:obj = %d, chunk_id = %d, n_bytes = %d, ignored",
+ blk, chunk_in_block, tags.obj_id,
+ tags.chunk_id, tags.n_bytes);
+ dev->n_free_chunks++;
+ } else if (tags.chunk_id > 0) {
+ /* chunk_id > 0 so it is a data chunk... */
+ loff_t endpos;
+ loff_t chunk_base = (tags.chunk_id - 1) *
+ dev->data_bytes_per_chunk;
+
+ *found_chunks = 1;
+
+ yaffs_set_chunk_bit(dev, blk, chunk_in_block);
+ bi->pages_in_use++;
+
+ in = yaffs_find_or_create_by_number(dev,
+ tags.obj_id,
+ YAFFS_OBJECT_TYPE_FILE);
+ if (!in)
+ /* Out of memory */
+ alloc_failed = 1;
+
+ if (in &&
+ in->variant_type == YAFFS_OBJECT_TYPE_FILE &&
+ chunk_base < in->variant.file_variant.shrink_size) {
+ /* This has not been invalidated by
+ * a resize */
+ if (!yaffs_put_chunk_in_file(in, tags.chunk_id,
+ chunk, -1))
+ alloc_failed = 1;
+
+ /* File size is calculated by looking at
+ * the data chunks if we have not
+ * seen an object header yet.
+ * Stop this practice once we find an
+ * object header.
+ */
+ endpos = chunk_base + tags.n_bytes;
+
+ if (!in->valid &&
+ in->variant.file_variant.scanned_size < endpos) {
+ in->variant.file_variant.
+ scanned_size = endpos;
+ in->variant.file_variant.
+ file_size = endpos;
+ }
+ } else if (in) {
+ /* This chunk has been invalidated by a
+ * resize, or a past file deletion
+ * so delete the chunk*/
+ yaffs_chunk_del(dev, chunk, 1, __LINE__);
+ }
+ } else {
+ /* chunk_id == 0, so it is an ObjectHeader.
+ * Thus, we read in the object header and make
+ * the object
+ */
+ *found_chunks = 1;
+
+ yaffs_set_chunk_bit(dev, blk, chunk_in_block);
+ bi->pages_in_use++;
+
+ oh = NULL;
+ in = NULL;
+
+ if (tags.extra_available) {
+ in = yaffs_find_or_create_by_number(dev,
+ tags.obj_id,
+ tags.extra_obj_type);
+ if (!in)
+ alloc_failed = 1;
+ }
+
+ if (!in ||
+ (!in->valid && dev->param.disable_lazy_load) ||
+ tags.extra_shadows ||
+ (!in->valid && (tags.obj_id == YAFFS_OBJECTID_ROOT ||
+ tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND))) {
+
+ /* If we don't have valid info then we
+ * need to read the chunk
+ * TODO In future we can probably defer
+ * reading the chunk and living with
+ * invalid data until needed.
+ */
+
+ yaffs_rd_chunk_tags_nand(dev, chunk, chunk_data, NULL);
+
+ oh = (struct yaffs_obj_hdr *)chunk_data;
+
+ if (dev->param.inband_tags) {
+ /* Fix up the header if they got
+ * corrupted by inband tags */
+ oh->shadows_obj =
+ oh->inband_shadowed_obj_id;
+ oh->is_shrink =
+ oh->inband_is_shrink;
+ }
+
+ if (!in) {
+ in = yaffs_find_or_create_by_number(dev,
+ tags.obj_id, oh->type);
+ if (!in)
+ alloc_failed = 1;
+ }
+ }
+
+ if (!in) {
+ /* TODO Hoosterman we have a problem! */
+ yaffs_trace(YAFFS_TRACE_ERROR,
+ "yaffs tragedy: Could not make object for object %d at chunk %d during scan",
+ tags.obj_id, chunk);
+ return YAFFS_FAIL;
+ }
+
+ if (in->valid) {
+ /* We have already filled this one.
+ * We have a duplicate that will be
+ * discarded, but we first have to suck
+ * out resize info if it is a file.
+ */
+ if ((in->variant_type == YAFFS_OBJECT_TYPE_FILE) &&
+ ((oh && oh->type == YAFFS_OBJECT_TYPE_FILE) ||
+ (tags.extra_available &&
+ tags.extra_obj_type == YAFFS_OBJECT_TYPE_FILE)
+ )) {
+ loff_t this_size = (oh) ?
+ yaffs_oh_to_size(oh) :
+ tags.extra_file_size;
+ u32 parent_obj_id = (oh) ?
+ oh->parent_obj_id :
+ tags.extra_parent_id;
+
+ is_shrink = (oh) ?
+ oh->is_shrink :
+ tags.extra_is_shrink;
+
+ /* If it is deleted (unlinked
+ * at start also means deleted)
+ * we treat the file size as
+ * being zeroed at this point.
+ */
+ if (parent_obj_id == YAFFS_OBJECTID_DELETED ||
+ parent_obj_id == YAFFS_OBJECTID_UNLINKED) {
+ this_size = 0;
+ is_shrink = 1;
+ }
+
+ if (is_shrink &&
+ in->variant.file_variant.shrink_size >
+ this_size)
+ in->variant.file_variant.shrink_size =
+ this_size;
+
+ if (is_shrink)
+ bi->has_shrink_hdr = 1;
+ }
+ /* Use existing - destroy this one. */
+ yaffs_chunk_del(dev, chunk, 1, __LINE__);
+ }
+
+ if (!in->valid && in->variant_type !=
+ (oh ? oh->type : tags.extra_obj_type))
+ yaffs_trace(YAFFS_TRACE_ERROR,
+ "yaffs tragedy: Bad object type, %d != %d, for object %d at chunk %d during scan",
+ oh ? oh->type : tags.extra_obj_type,
+ in->variant_type, tags.obj_id,
+ chunk);
+
+ if (!in->valid &&
+ (tags.obj_id == YAFFS_OBJECTID_ROOT ||
+ tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND)) {
+ /* We only load some info, don't fiddle
+ * with directory structure */
+ in->valid = 1;
+
+ if (oh) {
+ in->yst_mode = oh->yst_mode;
+ yaffs_load_attribs(in, oh);
+ in->lazy_loaded = 0;
+ } else {
+ in->lazy_loaded = 1;
+ }
+ in->hdr_chunk = chunk;
+
+ } else if (!in->valid) {
+ /* we need to load this info */
+ in->valid = 1;
+ in->hdr_chunk = chunk;
+ if (oh) {
+ in->variant_type = oh->type;
+ in->yst_mode = oh->yst_mode;
+ yaffs_load_attribs(in, oh);
+
+ if (oh->shadows_obj > 0)
+ yaffs_handle_shadowed_obj(dev,
+ oh->shadows_obj, 1);
+
+ yaffs_set_obj_name_from_oh(in, oh);
+ parent = yaffs_find_or_create_by_number(dev,
+ oh->parent_obj_id,
+ YAFFS_OBJECT_TYPE_DIRECTORY);
+ file_size = yaffs_oh_to_size(oh);
+ is_shrink = oh->is_shrink;
+ equiv_id = oh->equiv_id;
+ } else {
+ in->variant_type = tags.extra_obj_type;
+ parent = yaffs_find_or_create_by_number(dev,
+ tags.extra_parent_id,
+ YAFFS_OBJECT_TYPE_DIRECTORY);
+ file_size = tags.extra_file_size;
+ is_shrink = tags.extra_is_shrink;
+ equiv_id = tags.extra_equiv_id;
+ in->lazy_loaded = 1;
+ }
+ in->dirty = 0;
+
+ if (!parent)
+ alloc_failed = 1;
+
+ /* directory stuff...
+ * hook up to parent
+ */
+
+ if (parent &&
+ parent->variant_type == YAFFS_OBJECT_TYPE_UNKNOWN) {
+ /* Set up as a directory */
+ parent->variant_type =
+ YAFFS_OBJECT_TYPE_DIRECTORY;
+ INIT_LIST_HEAD(&parent->
+ variant.dir_variant.children);
+ } else if (!parent ||
+ parent->variant_type !=
+ YAFFS_OBJECT_TYPE_DIRECTORY) {
+ /* Hoosterman, another problem....
+ * Trying to use a non-directory as a directory
+ */
+
+ yaffs_trace(YAFFS_TRACE_ERROR,
+ "yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found."
+ );
+ parent = dev->lost_n_found;
+ }
+ yaffs_add_obj_to_dir(parent, in);
+
+ is_unlinked = (parent == dev->del_dir) ||
+ (parent == dev->unlinked_dir);
+
+ if (is_shrink)
+ /* Mark the block */
+ bi->has_shrink_hdr = 1;
+
+ /* Note re hardlinks.
+ * Since we might scan a hardlink before its equivalent
+ * object is scanned we put them all in a list.
+ * After scanning is complete, we should have all the
+ * objects, so we run through this list and fix up all
+ * the chains.
+ */
+
+ switch (in->variant_type) {
+ case YAFFS_OBJECT_TYPE_UNKNOWN:
+ /* Todo got a problem */
+ break;
+ case YAFFS_OBJECT_TYPE_FILE:
+ file_var = &in->variant.file_variant;
+ if (file_var->scanned_size < file_size) {
+ /* This covers the case where the file
+ * size is greater than the data held.
+ * This will happen if the file is
+ * resized to be larger than its
+ * current data extents.
+ */
+ file_var->file_size = file_size;
+ file_var->scanned_size = file_size;
+ }
+
+ if (file_var->shrink_size > file_size)
+ file_var->shrink_size = file_size;
+
+ break;
+ case YAFFS_OBJECT_TYPE_HARDLINK:
+ hl_var = &in->variant.hardlink_variant;
+ if (!is_unlinked) {
+ hl_var->equiv_id = equiv_id;
+ list_add(&in->hard_links, hard_list);
+ }
+ break;
+ case YAFFS_OBJECT_TYPE_DIRECTORY:
+ /* Do nothing */
+ break;
+ case YAFFS_OBJECT_TYPE_SPECIAL:
+ /* Do nothing */
+ break;
+ case YAFFS_OBJECT_TYPE_SYMLINK:
+ sl_var = &in->variant.symlink_variant;
+ if (oh) {
+ sl_var->alias =
+ yaffs_clone_str(oh->alias);
+ if (!sl_var->alias)
+ alloc_failed = 1;
+ }
+ break;
+ }
+ }
+ }
+ return alloc_failed ? YAFFS_FAIL : YAFFS_OK;
+}
+
+int yaffs2_scan_backwards(struct yaffs_dev *dev)
+{
+ int blk;
+ int block_iter;
+ int start_iter;
+ int end_iter;
+ int n_to_scan = 0;
+ enum yaffs_block_state state;
+ int c;
+ LIST_HEAD(hard_list);
+ struct yaffs_block_info *bi;
+ u32 seq_number;
+ int n_blocks = dev->internal_end_block - dev->internal_start_block + 1;
+ u8 *chunk_data;
+ int found_chunks;
+ int alloc_failed = 0;
+ struct yaffs_block_index *block_index = NULL;
+ int alt_block_index = 0;
+ int summary_available;
+
+ yaffs_trace(YAFFS_TRACE_SCAN,
+ "yaffs2_scan_backwards starts intstartblk %d intendblk %d...",
+ dev->internal_start_block, dev->internal_end_block);
+
+ dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER;
+
+ block_index =
+ kmalloc(n_blocks * sizeof(struct yaffs_block_index), GFP_NOFS);
+
+ if (!block_index) {
+ block_index =
+ vmalloc(n_blocks * sizeof(struct yaffs_block_index));
+ alt_block_index = 1;
+ }
+
+ if (!block_index) {
+ yaffs_trace(YAFFS_TRACE_SCAN,
+ "yaffs2_scan_backwards() could not allocate block index!"
+ );
+ return YAFFS_FAIL;
+ }
+
+ dev->blocks_in_checkpt = 0;
+
+ chunk_data = yaffs_get_temp_buffer(dev);
+
+ /* Scan all the blocks to determine their state */
+ bi = dev->block_info;
+ for (blk = dev->internal_start_block; blk <= dev->internal_end_block;
+ blk++) {
+ yaffs_clear_chunk_bits(dev, blk);
+ bi->pages_in_use = 0;
+ bi->soft_del_pages = 0;
+
+ yaffs_query_init_block_state(dev, blk, &state, &seq_number);
+
+ bi->block_state = state;
+ bi->seq_number = seq_number;
+
+ if (bi->seq_number == YAFFS_SEQUENCE_CHECKPOINT_DATA)
+ bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT;
+ if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK)
+ bi->block_state = YAFFS_BLOCK_STATE_DEAD;
+
+ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG,
+ "Block scanning block %d state %d seq %d",
+ blk, bi->block_state, seq_number);
+
+ if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) {
+ dev->blocks_in_checkpt++;
+
+ } else if (bi->block_state == YAFFS_BLOCK_STATE_DEAD) {
+ yaffs_trace(YAFFS_TRACE_BAD_BLOCKS,
+ "block %d is bad", blk);
+ } else if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) {
+ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty ");
+ dev->n_erased_blocks++;
+ dev->n_free_chunks += dev->param.chunks_per_block;
+ } else if (bi->block_state ==
+ YAFFS_BLOCK_STATE_NEEDS_SCAN) {
+ /* Determine the highest sequence number */
+ if (seq_number >= YAFFS_LOWEST_SEQUENCE_NUMBER &&
+ seq_number < YAFFS_HIGHEST_SEQUENCE_NUMBER) {
+ block_index[n_to_scan].seq = seq_number;
+ block_index[n_to_scan].block = blk;
+ n_to_scan++;
+ if (seq_number >= dev->seq_number)
+ dev->seq_number = seq_number;
+ } else {
+ /* TODO: Nasty sequence number! */
+ yaffs_trace(YAFFS_TRACE_SCAN,
+ "Block scanning block %d has bad sequence number %d",
+ blk, seq_number);
+ }
+ }
+ bi++;
+ }
+
+ yaffs_trace(YAFFS_TRACE_SCAN, "%d blocks to be sorted...", n_to_scan);
+
+ cond_resched();
+
+ /* Sort the blocks by sequence number */
+ sort(block_index, n_to_scan, sizeof(struct yaffs_block_index),
+ yaffs2_ybicmp, NULL);
+
+ cond_resched();
+
+ yaffs_trace(YAFFS_TRACE_SCAN, "...done");
+
+ /* Now scan the blocks looking at the data. */
+ start_iter = 0;
+ end_iter = n_to_scan - 1;
+ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "%d blocks to scan", n_to_scan);
+
+ /* For each block.... backwards */
+ for (block_iter = end_iter;
+ !alloc_failed && block_iter >= start_iter;
+ block_iter--) {
+ /* Cooperative multitasking! This loop can run for so
+ long that watchdog timers expire. */
+ cond_resched();
+
+ /* get the block to scan in the correct order */
+ blk = block_index[block_iter].block;
+ bi = yaffs_get_block_info(dev, blk);
+
+ summary_available = yaffs_summary_read(dev, dev->sum_tags, blk);
+
+ /* For each chunk in each block that needs scanning.... */
+ found_chunks = 0;
+ if (summary_available)
+ c = dev->chunks_per_summary - 1;
+ else
+ c = dev->param.chunks_per_block - 1;
+
+ for (/* c is already initialised */;
+ !alloc_failed && c >= 0 &&
+ (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN ||
+ bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING);
+ c--) {
+ /* Scan backwards...
+ * Read the tags and decide what to do
+ */
+ if (yaffs2_scan_chunk(dev, bi, blk, c,
+ &found_chunks, chunk_data,
+ &hard_list, summary_available) ==
+ YAFFS_FAIL)
+ alloc_failed = 1;
+ }
+
+ if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN) {
+ /* If we got this far while scanning, then the block
+ * is fully allocated. */
+ bi->block_state = YAFFS_BLOCK_STATE_FULL;
+ }
+
+ /* Now let's see if it was dirty */
+ if (bi->pages_in_use == 0 &&
+ !bi->has_shrink_hdr &&
+ bi->block_state == YAFFS_BLOCK_STATE_FULL) {
+ yaffs_block_became_dirty(dev, blk);
+ }
+ }
+
+ yaffs_skip_rest_of_block(dev);
+
+ if (alt_block_index)
+ vfree(block_index);
+ else
+ kfree(block_index);
+
+ /* Ok, we've done all the scanning.
+ * Fix up the hard link chains.
+ * We have scanned all the objects, now it's time to add these
+ * hardlinks.
+ */
+ yaffs_link_fixup(dev, &hard_list);
+
+ yaffs_release_temp_buffer(dev, chunk_data);
+
+ if (alloc_failed)
+ return YAFFS_FAIL;
+
+ yaffs_trace(YAFFS_TRACE_SCAN, "yaffs2_scan_backwards ends");
+
+ return YAFFS_OK;
+}
diff --git a/roms/u-boot/fs/yaffs2/yaffs_yaffs2.h b/roms/u-boot/fs/yaffs2/yaffs_yaffs2.h
new file mode 100644
index 000000000..2363bfd8b
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffs_yaffs2.h
@@ -0,0 +1,39 @@
+/*
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ */
+
+#ifndef __YAFFS_YAFFS2_H__
+#define __YAFFS_YAFFS2_H__
+
+#include "yaffs_guts.h"
+
+void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev);
+void yaffs2_find_oldest_dirty_seq(struct yaffs_dev *dev);
+void yaffs2_clear_oldest_dirty_seq(struct yaffs_dev *dev,
+ struct yaffs_block_info *bi);
+void yaffs2_update_oldest_dirty_seq(struct yaffs_dev *dev, unsigned block_no,
+ struct yaffs_block_info *bi);
+int yaffs_block_ok_for_gc(struct yaffs_dev *dev, struct yaffs_block_info *bi);
+u32 yaffs2_find_refresh_block(struct yaffs_dev *dev);
+int yaffs2_checkpt_required(struct yaffs_dev *dev);
+int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev);
+
+void yaffs2_checkpt_invalidate(struct yaffs_dev *dev);
+int yaffs2_checkpt_save(struct yaffs_dev *dev);
+int yaffs2_checkpt_restore(struct yaffs_dev *dev);
+
+int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size);
+int yaffs2_scan_backwards(struct yaffs_dev *dev);
+
+#endif
diff --git a/roms/u-boot/fs/yaffs2/yaffscfg.h b/roms/u-boot/fs/yaffs2/yaffscfg.h
new file mode 100644
index 000000000..718504eea
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffscfg.h
@@ -0,0 +1,38 @@
+/*
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ */
+
+/*
+ * Header file for using yaffs in an application via
+ * a direct interface.
+ */
+
+
+#ifndef __YAFFSCFG_H__
+#define __YAFFSCFG_H__
+
+
+#include "yportenv.h"
+
+#define YAFFSFS_N_HANDLES 100
+#define YAFFSFS_N_DSC 20
+
+
+struct yaffsfs_DeviceConfiguration {
+ const YCHAR *prefix;
+ struct yaffs_dev *dev;
+};
+
+
+#endif
diff --git a/roms/u-boot/fs/yaffs2/yaffsfs.c b/roms/u-boot/fs/yaffs2/yaffsfs.c
new file mode 100644
index 000000000..510faaeed
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffsfs.c
@@ -0,0 +1,3209 @@
+/*
+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <div64.h>
+#include "yaffsfs.h"
+#include "yaffs_guts.h"
+#include "yaffscfg.h"
+#include "yportenv.h"
+#include "yaffs_trace.h"
+#include <dm/devres.h>
+
+#define YAFFSFS_MAX_SYMLINK_DEREFERENCES 5
+
+#ifndef NULL
+#define NULL ((void *)0)
+#endif
+
+/* YAFFSFS_RW_SIZE must be a power of 2 */
+#define YAFFSFS_RW_SHIFT (13)
+#define YAFFSFS_RW_SIZE (1<<YAFFSFS_RW_SHIFT)
+
+/* Some forward references */
+static struct yaffs_obj *yaffsfs_FindObject(struct yaffs_obj *relativeDirectory,
+ const YCHAR *path,
+ int symDepth, int getEquiv,
+ struct yaffs_obj **dirOut,
+ int *notDir, int *loop);
+
+static void yaffsfs_RemoveObjectCallback(struct yaffs_obj *obj);
+
+unsigned int yaffs_wr_attempts;
+
+/*
+ * Handle management.
+ * There are open inodes in struct yaffsfs_Inode.
+ * There are open file descriptors in yaffsfs_FileDes.
+ * There are open handles in yaffsfs_FileDes.
+ *
+ * Things are structured this way to be like the Linux VFS model
+ * so that interactions with the yaffs guts calls are similar.
+ * That means more common code paths and less special code.
+ * That means better testing etc.
+ *
+ * We have 3 layers because:
+ * A handle is different than an fd because you can use dup()
+ * to create a new handle that accesses the *same* fd. The two
+ * handles will use the same offset (part of the fd). We only close
+ * down the fd when there are no more handles accessing it.
+ *
+ * More than one fd can currently access one file, but each fd
+ * has its own permsiions and offset.
+ */
+
+struct yaffsfs_Inode {
+ int count; /* Number of handles accessing this inode */
+ struct yaffs_obj *iObj;
+};
+
+struct yaffsfs_FileDes {
+ u8 reading:1;
+ u8 writing:1;
+ u8 append:1;
+ u8 shareRead:1;
+ u8 shareWrite:1;
+ int inodeId:12; /* Index to corresponding yaffsfs_Inode */
+ int handleCount:10; /* Number of handles for this fd */
+ loff_t position; /* current position in file */
+};
+
+struct yaffsfs_Handle {
+ short int fdId;
+ short int useCount;
+};
+
+
+struct yaffsfs_DirSearchContxt {
+ struct yaffs_dirent de; /* directory entry */
+ YCHAR name[NAME_MAX + 1]; /* name of directory being searched */
+ struct yaffs_obj *dirObj; /* ptr to directory being searched */
+ struct yaffs_obj *nextReturn; /* obj returned by next readddir */
+ struct list_head others;
+ int offset:20;
+ unsigned inUse:1;
+};
+
+static struct yaffsfs_DirSearchContxt yaffsfs_dsc[YAFFSFS_N_DSC];
+static struct yaffsfs_Inode yaffsfs_inode[YAFFSFS_N_HANDLES];
+static struct yaffsfs_FileDes yaffsfs_fd[YAFFSFS_N_HANDLES];
+static struct yaffsfs_Handle yaffsfs_handle[YAFFSFS_N_HANDLES];
+
+static int yaffsfs_handlesInitialised;
+
+unsigned yaffs_set_trace(unsigned tm)
+{
+ yaffs_trace_mask = tm;
+ return yaffs_trace_mask;
+}
+
+unsigned yaffs_get_trace(void)
+{
+ return yaffs_trace_mask;
+}
+
+/*
+ * yaffsfs_InitHandle
+ * Inilitalise handle management on start-up.
+ */
+
+static void yaffsfs_InitHandles(void)
+{
+ int i;
+ if (yaffsfs_handlesInitialised)
+ return;
+
+ memset(yaffsfs_inode, 0, sizeof(yaffsfs_inode));
+ memset(yaffsfs_fd, 0, sizeof(yaffsfs_fd));
+ memset(yaffsfs_handle, 0, sizeof(yaffsfs_handle));
+ memset(yaffsfs_dsc, 0, sizeof(yaffsfs_dsc));
+
+ for (i = 0; i < YAFFSFS_N_HANDLES; i++)
+ yaffsfs_fd[i].inodeId = -1;
+ for (i = 0; i < YAFFSFS_N_HANDLES; i++)
+ yaffsfs_handle[i].fdId = -1;
+}
+
+static struct yaffsfs_Handle *yaffsfs_HandleToPointer(int h)
+{
+ if (h >= 0 && h < YAFFSFS_N_HANDLES)
+ return &yaffsfs_handle[h];
+ return NULL;
+}
+
+static struct yaffsfs_FileDes *yaffsfs_HandleToFileDes(int handle)
+{
+ struct yaffsfs_Handle *h = yaffsfs_HandleToPointer(handle);
+
+ if (h && h->useCount > 0 && h->fdId >= 0 && h->fdId < YAFFSFS_N_HANDLES)
+ return &yaffsfs_fd[h->fdId];
+
+ return NULL;
+}
+
+static struct yaffsfs_Inode *yaffsfs_HandleToInode(int handle)
+{
+ struct yaffsfs_FileDes *fd = yaffsfs_HandleToFileDes(handle);
+
+ if (fd && fd->handleCount > 0 &&
+ fd->inodeId >= 0 && fd->inodeId < YAFFSFS_N_HANDLES)
+ return &yaffsfs_inode[fd->inodeId];
+
+ return NULL;
+}
+
+static struct yaffs_obj *yaffsfs_HandleToObject(int handle)
+{
+ struct yaffsfs_Inode *in = yaffsfs_HandleToInode(handle);
+
+ if (in)
+ return in->iObj;
+
+ return NULL;
+}
+
+/*
+ * yaffsfs_FindInodeIdForObject
+ * Find the inode entry for an object, if it exists.
+ */
+
+static int yaffsfs_FindInodeIdForObject(struct yaffs_obj *obj)
+{
+ int i;
+ int ret = -1;
+
+ if (obj)
+ obj = yaffs_get_equivalent_obj(obj);
+
+ /* Look for it in open inode table */
+ for (i = 0; i < YAFFSFS_N_HANDLES && ret < 0; i++) {
+ if (yaffsfs_inode[i].iObj == obj)
+ ret = i;
+ }
+ return ret;
+}
+
+/*
+ * yaffsfs_GetInodeIdForObject
+ * Grab an inode entry when opening a new inode.
+ */
+static int yaffsfs_GetInodeIdForObject(struct yaffs_obj *obj)
+{
+ int i;
+ int ret;
+ struct yaffsfs_Inode *in = NULL;
+
+ if (obj)
+ obj = yaffs_get_equivalent_obj(obj);
+
+ ret = yaffsfs_FindInodeIdForObject(obj);
+
+ for (i = 0; i < YAFFSFS_N_HANDLES && ret < 0; i++) {
+ if (!yaffsfs_inode[i].iObj)
+ ret = i;
+ }
+
+ if (ret >= 0) {
+ in = &yaffsfs_inode[ret];
+ if (!in->iObj)
+ in->count = 0;
+ in->iObj = obj;
+ in->count++;
+ }
+
+ return ret;
+}
+
+static int yaffsfs_CountHandles(struct yaffs_obj *obj)
+{
+ int i = yaffsfs_FindInodeIdForObject(obj);
+
+ if (i >= 0)
+ return yaffsfs_inode[i].count;
+ else
+ return 0;
+}
+
+static void yaffsfs_ReleaseInode(struct yaffsfs_Inode *in)
+{
+ struct yaffs_obj *obj;
+
+ obj = in->iObj;
+
+ if (obj->unlinked)
+ yaffs_del_obj(obj);
+
+ obj->my_inode = NULL;
+ in->iObj = NULL;
+
+}
+
+static void yaffsfs_PutInode(int inodeId)
+{
+ if (inodeId >= 0 && inodeId < YAFFSFS_N_HANDLES) {
+ struct yaffsfs_Inode *in = &yaffsfs_inode[inodeId];
+ in->count--;
+ if (in->count <= 0) {
+ yaffsfs_ReleaseInode(in);
+ in->count = 0;
+ }
+ }
+}
+
+static int yaffsfs_NewHandle(struct yaffsfs_Handle **hptr)
+{
+ int i;
+ struct yaffsfs_Handle *h;
+
+ for (i = 0; i < YAFFSFS_N_HANDLES; i++) {
+ h = &yaffsfs_handle[i];
+ if (h->useCount < 1) {
+ memset(h, 0, sizeof(struct yaffsfs_Handle));
+ h->fdId = -1;
+ h->useCount = 1;
+ if (hptr)
+ *hptr = h;
+ return i;
+ }
+ }
+ return -1;
+}
+
+static int yaffsfs_NewHandleAndFileDes(void)
+{
+ int i;
+ struct yaffsfs_FileDes *fd;
+ struct yaffsfs_Handle *h = NULL;
+ int handle = yaffsfs_NewHandle(&h);
+
+ if (handle < 0)
+ return -1;
+
+ for (i = 0; i < YAFFSFS_N_HANDLES; i++) {
+ fd = &yaffsfs_fd[i];
+ if (fd->handleCount < 1) {
+ memset(fd, 0, sizeof(struct yaffsfs_FileDes));
+ fd->inodeId = -1;
+ fd->handleCount = 1;
+ h->fdId = i;
+ return handle;
+ }
+ }
+
+ /* Dump the handle because we could not get a fd */
+ h->useCount = 0;
+ return -1;
+}
+
+/*
+ * yaffs_get_handle
+ * Increase use of handle when reading/writing a file
+ * Also gets the file descriptor.
+ */
+
+static int yaffsfs_GetHandle(int handle)
+{
+ struct yaffsfs_Handle *h = yaffsfs_HandleToPointer(handle);
+
+ if (h && h->useCount > 0) {
+ h->useCount++;
+ return 0;
+ }
+ return -1;
+}
+
+/*
+ * yaffs_put_handle
+ * Let go of a handle when closing a file or aborting an open or
+ * ending a read or write.
+ */
+
+static int yaffsfs_PutFileDes(int fdId)
+{
+ struct yaffsfs_FileDes *fd;
+
+ if (fdId >= 0 && fdId < YAFFSFS_N_HANDLES) {
+ fd = &yaffsfs_fd[fdId];
+ fd->handleCount--;
+ if (fd->handleCount < 1) {
+ if (fd->inodeId >= 0) {
+ yaffsfs_PutInode(fd->inodeId);
+ fd->inodeId = -1;
+ }
+ }
+ }
+ return 0;
+}
+
+static int yaffsfs_PutHandle(int handle)
+{
+ struct yaffsfs_Handle *h = yaffsfs_HandleToPointer(handle);
+
+ if (h && h->useCount > 0) {
+ h->useCount--;
+ if (h->useCount < 1) {
+ yaffsfs_PutFileDes(h->fdId);
+ h->fdId = -1;
+ }
+ }
+
+ return 0;
+}
+
+static void yaffsfs_BreakDeviceHandles(struct yaffs_dev *dev)
+{
+ struct yaffsfs_FileDes *fd;
+ struct yaffsfs_Handle *h;
+ struct yaffs_obj *obj;
+ int i;
+ for (i = 0; i < YAFFSFS_N_HANDLES; i++) {
+ h = yaffsfs_HandleToPointer(i);
+ fd = yaffsfs_HandleToFileDes(i);
+ obj = yaffsfs_HandleToObject(i);
+ if (h && h->useCount > 0) {
+ h->useCount = 0;
+ h->fdId = 0;
+ }
+ if (fd && fd->handleCount > 0 && obj && obj->my_dev == dev) {
+ fd->handleCount = 0;
+ yaffsfs_PutInode(fd->inodeId);
+ fd->inodeId = -1;
+ }
+ }
+}
+
+/*
+ * Stuff to handle names.
+ */
+#ifdef CONFIG_YAFFS_CASE_INSENSITIVE
+
+static int yaffs_toupper(YCHAR a)
+{
+ if (a >= 'a' && a <= 'z')
+ return (a - 'a') + 'A';
+ else
+ return a;
+}
+
+int yaffsfs_Match(YCHAR a, YCHAR b)
+{
+ return (yaffs_toupper(a) == yaffs_toupper(b));
+}
+#else
+int yaffsfs_Match(YCHAR a, YCHAR b)
+{
+ /* case sensitive */
+ return (a == b);
+}
+#endif
+
+int yaffsfs_IsPathDivider(YCHAR ch)
+{
+ const YCHAR *str = YAFFS_PATH_DIVIDERS;
+
+ while (*str) {
+ if (*str == ch)
+ return 1;
+ str++;
+ }
+
+ return 0;
+}
+
+int yaffsfs_CheckNameLength(const char *name)
+{
+ int retVal = 0;
+
+ int nameLength = yaffs_strnlen(name, YAFFS_MAX_NAME_LENGTH + 1);
+
+ if (nameLength == 0) {
+ yaffsfs_SetError(-ENOENT);
+ retVal = -1;
+ } else if (nameLength > YAFFS_MAX_NAME_LENGTH) {
+ yaffsfs_SetError(-ENAMETOOLONG);
+ retVal = -1;
+ }
+
+ return retVal;
+}
+
+static int yaffsfs_alt_dir_path(const YCHAR *path, YCHAR **ret_path)
+{
+ YCHAR *alt_path = NULL;
+ int path_length;
+ int i;
+
+ /*
+ * We don't have a definition for max path length.
+ * We will use 3 * max name length instead.
+ */
+ *ret_path = NULL;
+ path_length = yaffs_strnlen(path, (YAFFS_MAX_NAME_LENGTH + 1) * 3 + 1);
+
+ /* If the last character is a path divider, then we need to
+ * trim it back so that the name look-up works properly.
+ * eg. /foo/new_dir/ -> /foo/newdir
+ * Curveball: Need to handle multiple path dividers:
+ * eg. /foof/sdfse///// -> /foo/sdfse
+ */
+ if (path_length > 0 && yaffsfs_IsPathDivider(path[path_length - 1])) {
+ alt_path = kmalloc(path_length + 1, 0);
+ if (!alt_path)
+ return -1;
+ yaffs_strcpy(alt_path, path);
+ for (i = path_length - 1;
+ i >= 0 && yaffsfs_IsPathDivider(alt_path[i]); i--)
+ alt_path[i] = (YCHAR) 0;
+ }
+ *ret_path = alt_path;
+ return 0;
+}
+
+LIST_HEAD(yaffsfs_deviceList);
+
+/*
+ * yaffsfs_FindDevice
+ * yaffsfs_FindRoot
+ * Scan the configuration list to find the device
+ * Curveballs: Should match paths that end in '/' too
+ * Curveball2 Might have "/x/ and "/x/y". Need to return the longest match
+ */
+static struct yaffs_dev *yaffsfs_FindDevice(const YCHAR *path,
+ YCHAR **restOfPath)
+{
+ struct list_head *cfg;
+ const YCHAR *leftOver;
+ const YCHAR *p;
+ struct yaffs_dev *retval = NULL;
+ struct yaffs_dev *dev = NULL;
+ int thisMatchLength;
+ int longestMatch = -1;
+ int matching;
+
+ /*
+ * Check all configs, choose the one that:
+ * 1) Actually matches a prefix (ie /a amd /abc will not match
+ * 2) Matches the longest.
+ */
+ list_for_each(cfg, &yaffsfs_deviceList) {
+ dev = list_entry(cfg, struct yaffs_dev, dev_list);
+ leftOver = path;
+ p = dev->param.name;
+ thisMatchLength = 0;
+ matching = 1;
+
+ while (matching && *p && *leftOver) {
+ /* Skip over any /s */
+ while (yaffsfs_IsPathDivider(*p))
+ p++;
+
+ /* Skip over any /s */
+ while (yaffsfs_IsPathDivider(*leftOver))
+ leftOver++;
+
+ /* Now match the text part */
+ while (matching &&
+ *p && !yaffsfs_IsPathDivider(*p) &&
+ *leftOver && !yaffsfs_IsPathDivider(*leftOver)) {
+ if (yaffsfs_Match(*p, *leftOver)) {
+ p++;
+ leftOver++;
+ thisMatchLength++;
+ } else {
+ matching = 0;
+ }
+ }
+ }
+
+ /* Skip over any /s in leftOver */
+ while (yaffsfs_IsPathDivider(*leftOver))
+ leftOver++;
+
+ /*Skip over any /s in p */
+ while (yaffsfs_IsPathDivider(*p))
+ p++;
+
+ /* p should now be at the end of the string if fully matched */
+ if (*p)
+ matching = 0;
+
+ if (matching && (thisMatchLength > longestMatch)) {
+ /* Matched prefix */
+ *restOfPath = (YCHAR *) leftOver;
+ retval = dev;
+ longestMatch = thisMatchLength;
+ }
+
+ }
+ return retval;
+}
+
+static int yaffsfs_CheckPath(const YCHAR *path)
+{
+ int n = 0;
+ int divs = 0;
+
+ while (*path && n < YAFFS_MAX_NAME_LENGTH && divs < 100) {
+ if (yaffsfs_IsPathDivider(*path)) {
+ n = 0;
+ divs++;
+ } else
+ n++;
+ path++;
+ }
+
+ return (*path) ? -1 : 0;
+}
+
+/* FindMountPoint only returns a dev entry if the path is a mount point */
+static struct yaffs_dev *yaffsfs_FindMountPoint(const YCHAR *path)
+{
+ struct yaffs_dev *dev;
+ YCHAR *restOfPath = NULL;
+
+ dev = yaffsfs_FindDevice(path, &restOfPath);
+ if (dev && restOfPath && *restOfPath)
+ dev = NULL;
+ return dev;
+}
+
+static struct yaffs_obj *yaffsfs_FindRoot(const YCHAR *path,
+ YCHAR **restOfPath)
+{
+ struct yaffs_dev *dev;
+
+ dev = yaffsfs_FindDevice(path, restOfPath);
+ if (dev && dev->is_mounted)
+ return dev->root_dir;
+
+ return NULL;
+}
+
+static struct yaffs_obj *yaffsfs_FollowLink(struct yaffs_obj *obj,
+ int symDepth, int *loop)
+{
+
+ if (obj)
+ obj = yaffs_get_equivalent_obj(obj);
+
+ while (obj && obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) {
+ YCHAR *alias = obj->variant.symlink_variant.alias;
+
+ if (yaffsfs_IsPathDivider(*alias))
+ /* Starts with a /, need to scan from root up */
+ obj = yaffsfs_FindObject(NULL, alias, symDepth++,
+ 1, NULL, NULL, loop);
+ else
+ /*
+ * Relative to here so use the parent of the
+ * symlink as a start
+ */
+ obj = yaffsfs_FindObject(obj->parent, alias, symDepth++,
+ 1, NULL, NULL, loop);
+ }
+ return obj;
+}
+
+/*
+ * yaffsfs_FindDirectory
+ * Parse a path to determine the directory and the name within the directory.
+ *
+ * eg. "/data/xx/ff" --> puts name="ff" and returns the directory "/data/xx"
+ */
+static struct yaffs_obj *yaffsfs_DoFindDirectory(struct yaffs_obj *startDir,
+ const YCHAR *path,
+ YCHAR **name, int symDepth,
+ int *notDir, int *loop)
+{
+ struct yaffs_obj *dir;
+ YCHAR *restOfPath;
+ YCHAR str[YAFFS_MAX_NAME_LENGTH + 1];
+ int i;
+
+ if (symDepth > YAFFSFS_MAX_SYMLINK_DEREFERENCES) {
+ if (loop)
+ *loop = 1;
+ return NULL;
+ }
+
+ if (startDir) {
+ dir = startDir;
+ restOfPath = (YCHAR *) path;
+ } else
+ dir = yaffsfs_FindRoot(path, &restOfPath);
+
+ while (dir) {
+ /*
+ * parse off /.
+ * curve ball: also throw away surplus '/'
+ * eg. "/ram/x////ff" gets treated the same as "/ram/x/ff"
+ */
+ while (yaffsfs_IsPathDivider(*restOfPath))
+ restOfPath++; /* get rid of '/' */
+
+ *name = restOfPath;
+ i = 0;
+
+ while (*restOfPath && !yaffsfs_IsPathDivider(*restOfPath)) {
+ if (i < YAFFS_MAX_NAME_LENGTH) {
+ str[i] = *restOfPath;
+ str[i + 1] = '\0';
+ i++;
+ }
+ restOfPath++;
+ }
+
+ if (!*restOfPath)
+ /* got to the end of the string */
+ return dir;
+ else {
+ if (yaffs_strcmp(str, _Y(".")) == 0) {
+ /* Do nothing */
+ } else if (yaffs_strcmp(str, _Y("..")) == 0) {
+ dir = dir->parent;
+ } else {
+ dir = yaffs_find_by_name(dir, str);
+
+ dir = yaffsfs_FollowLink(dir, symDepth, loop);
+
+ if (dir && dir->variant_type !=
+ YAFFS_OBJECT_TYPE_DIRECTORY) {
+ if (notDir)
+ *notDir = 1;
+ dir = NULL;
+ }
+
+ }
+ }
+ }
+ /* directory did not exist. */
+ return NULL;
+}
+
+static struct yaffs_obj *yaffsfs_FindDirectory(struct yaffs_obj *relDir,
+ const YCHAR *path,
+ YCHAR **name,
+ int symDepth,
+ int *notDir, int *loop)
+{
+ return yaffsfs_DoFindDirectory(relDir, path, name, symDepth, notDir,
+ loop);
+}
+
+/*
+ * yaffsfs_FindObject turns a path for an existing object into the object
+ */
+static struct yaffs_obj *yaffsfs_FindObject(struct yaffs_obj *relDir,
+ const YCHAR *path, int symDepth,
+ int getEquiv,
+ struct yaffs_obj **dirOut,
+ int *notDir, int *loop)
+{
+ struct yaffs_obj *dir;
+ struct yaffs_obj *obj;
+ YCHAR *name;
+
+ dir =
+ yaffsfs_FindDirectory(relDir, path, &name, symDepth, notDir, loop);
+
+ if (dirOut)
+ *dirOut = dir;
+
+ if (dir && *name)
+ obj = yaffs_find_by_name(dir, name);
+ else
+ obj = dir;
+
+ if (getEquiv)
+ obj = yaffs_get_equivalent_obj(obj);
+
+ return obj;
+}
+
+/*************************************************************************
+ * Start of yaffsfs visible functions.
+ *************************************************************************/
+
+int yaffs_dup(int handle)
+{
+ int newHandleNumber = -1;
+ struct yaffsfs_FileDes *existingFD = NULL;
+ struct yaffsfs_Handle *existingHandle = NULL;
+ struct yaffsfs_Handle *newHandle = NULL;
+
+ yaffsfs_Lock();
+ existingHandle = yaffsfs_HandleToPointer(handle);
+ existingFD = yaffsfs_HandleToFileDes(handle);
+ if (existingFD)
+ newHandleNumber = yaffsfs_NewHandle(&newHandle);
+ if (newHandle) {
+ newHandle->fdId = existingHandle->fdId;
+ existingFD->handleCount++;
+ }
+
+ yaffsfs_Unlock();
+
+ if (!existingFD)
+ yaffsfs_SetError(-EBADF);
+ else if (!newHandle)
+ yaffsfs_SetError(-ENOMEM);
+
+ return newHandleNumber;
+
+}
+
+static int yaffsfs_TooManyObjects(struct yaffs_dev *dev)
+{
+ int current_objects = dev->n_obj - dev->n_deleted_files;
+
+ if (dev->param.max_objects && current_objects > dev->param.max_objects)
+ return 1;
+ else
+ return 0;
+}
+
+int yaffs_open_sharing(const YCHAR *path, int oflag, int mode, int sharing)
+{
+ struct yaffs_obj *obj = NULL;
+ struct yaffs_obj *dir = NULL;
+ YCHAR *name;
+ int handle = -1;
+ struct yaffsfs_FileDes *fd = NULL;
+ int openDenied = 0;
+ int symDepth = 0;
+ int errorReported = 0;
+ int rwflags = oflag & (O_RDWR | O_RDONLY | O_WRONLY);
+ u8 shareRead = (sharing & YAFFS_SHARE_READ) ? 1 : 0;
+ u8 shareWrite = (sharing & YAFFS_SHARE_WRITE) ? 1 : 0;
+ u8 sharedReadAllowed;
+ u8 sharedWriteAllowed;
+ u8 alreadyReading;
+ u8 alreadyWriting;
+ u8 readRequested;
+ u8 writeRequested;
+ int notDir = 0;
+ int loop = 0;
+
+ if (!path) {
+ yaffsfs_SetError(-EFAULT);
+ return -1;
+ }
+
+ if (yaffsfs_CheckPath(path) < 0) {
+ yaffsfs_SetError(-ENAMETOOLONG);
+ return -1;
+ }
+
+ /* O_EXCL only has meaning if O_CREAT is specified */
+ if (!(oflag & O_CREAT))
+ oflag &= ~(O_EXCL);
+
+ /* O_TRUNC has no meaning if (O_CREAT | O_EXCL) is specified */
+ if ((oflag & O_CREAT) & (oflag & O_EXCL))
+ oflag &= ~(O_TRUNC);
+
+ /* Todo: Are there any more flag combos to sanitise ? */
+
+ /* Figure out if reading or writing is requested */
+
+ readRequested = (rwflags == O_RDWR || rwflags == O_RDONLY) ? 1 : 0;
+ writeRequested = (rwflags == O_RDWR || rwflags == O_WRONLY) ? 1 : 0;
+
+ yaffsfs_Lock();
+
+ handle = yaffsfs_NewHandleAndFileDes();
+
+ if (handle < 0) {
+ yaffsfs_SetError(-ENFILE);
+ errorReported = 1;
+ } else {
+
+ fd = yaffsfs_HandleToFileDes(handle);
+
+ /* try to find the exisiting object */
+ obj = yaffsfs_FindObject(NULL, path, 0, 1, NULL, NULL, NULL);
+
+ obj = yaffsfs_FollowLink(obj, symDepth++, &loop);
+
+ if (obj &&
+ obj->variant_type != YAFFS_OBJECT_TYPE_FILE &&
+ obj->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY)
+ obj = NULL;
+
+ if (obj) {
+
+ /* The file already exists or it might be a directory */
+
+ /* A directory can't be opened as a file */
+ if (obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) {
+ openDenied = 1;
+ yaffsfs_SetError(-EISDIR);
+ errorReported = 1;
+ }
+
+ /* Open should fail if O_CREAT and O_EXCL are specified
+ * for a file that exists.
+ */
+ if (!errorReported &&
+ (oflag & O_EXCL) && (oflag & O_CREAT)) {
+ openDenied = 1;
+ yaffsfs_SetError(-EEXIST);
+ errorReported = 1;
+ }
+
+ /* Check file permissions */
+ if (readRequested && !(obj->yst_mode & S_IREAD))
+ openDenied = 1;
+
+ if (writeRequested && !(obj->yst_mode & S_IWRITE))
+ openDenied = 1;
+
+ if (!errorReported && writeRequested &&
+ obj->my_dev->read_only) {
+ openDenied = 1;
+ yaffsfs_SetError(-EROFS);
+ errorReported = 1;
+ }
+
+ if (openDenied && !errorReported) {
+ yaffsfs_SetError(-EACCES);
+ errorReported = 1;
+ }
+
+ /* Check sharing of an existing object. */
+ if (!openDenied) {
+ struct yaffsfs_FileDes *fdx;
+ int i;
+
+ sharedReadAllowed = 1;
+ sharedWriteAllowed = 1;
+ alreadyReading = 0;
+ alreadyWriting = 0;
+ for (i = 0; i < YAFFSFS_N_HANDLES; i++) {
+ fdx = &yaffsfs_fd[i];
+ if (fdx->handleCount > 0 &&
+ fdx->inodeId >= 0 &&
+ yaffsfs_inode[fdx->inodeId].iObj
+ == obj) {
+ if (!fdx->shareRead)
+ sharedReadAllowed = 0;
+ if (!fdx->shareWrite)
+ sharedWriteAllowed = 0;
+ if (fdx->reading)
+ alreadyReading = 1;
+ if (fdx->writing)
+ alreadyWriting = 1;
+ }
+ }
+
+ if ((!sharedReadAllowed && readRequested) ||
+ (!shareRead && alreadyReading) ||
+ (!sharedWriteAllowed && writeRequested) ||
+ (!shareWrite && alreadyWriting)) {
+ openDenied = 1;
+ yaffsfs_SetError(-EBUSY);
+ errorReported = 1;
+ }
+ }
+
+ }
+
+ /* If we could not open an existing object, then let's see if
+ * the directory exists. If not, error.
+ */
+ if (!obj && !errorReported) {
+ dir = yaffsfs_FindDirectory(NULL, path, &name, 0,
+ &notDir, &loop);
+ if (!dir && notDir) {
+ yaffsfs_SetError(-ENOTDIR);
+ errorReported = 1;
+ } else if (loop) {
+ yaffsfs_SetError(-ELOOP);
+ errorReported = 1;
+ } else if (!dir) {
+ yaffsfs_SetError(-ENOENT);
+ errorReported = 1;
+ }
+ }
+
+ if (!obj && dir && !errorReported && (oflag & O_CREAT)) {
+ /* Let's see if we can create this file */
+ if (dir->my_dev->read_only) {
+ yaffsfs_SetError(-EROFS);
+ errorReported = 1;
+ } else if (yaffsfs_TooManyObjects(dir->my_dev)) {
+ yaffsfs_SetError(-ENFILE);
+ errorReported = 1;
+ } else
+ obj = yaffs_create_file(dir, name, mode, 0, 0);
+
+ if (!obj && !errorReported) {
+ yaffsfs_SetError(-ENOSPC);
+ errorReported = 1;
+ }
+ }
+
+ if (!obj && dir && !errorReported && !(oflag & O_CREAT)) {
+ yaffsfs_SetError(-ENOENT);
+ errorReported = 1;
+ }
+
+ if (obj && !openDenied) {
+ int inodeId = yaffsfs_GetInodeIdForObject(obj);
+
+ if (inodeId < 0) {
+ /*
+ * Todo: Fix any problem if inodes run out,
+ * That can't happen if the number of inode
+ * items >= number of handles.
+ */
+ }
+
+ fd->inodeId = inodeId;
+ fd->reading = readRequested;
+ fd->writing = writeRequested;
+ fd->append = (oflag & O_APPEND) ? 1 : 0;
+ fd->position = 0;
+ fd->shareRead = shareRead;
+ fd->shareWrite = shareWrite;
+
+ /* Hook inode to object */
+ obj->my_inode = (void *)&yaffsfs_inode[inodeId];
+
+ if ((oflag & O_TRUNC) && fd->writing)
+ yaffs_resize_file(obj, 0);
+ } else {
+ yaffsfs_PutHandle(handle);
+ if (!errorReported)
+ yaffsfs_SetError(0); /* Problem */
+ handle = -1;
+ }
+ }
+
+ yaffsfs_Unlock();
+
+ return handle;
+}
+
+int yaffs_open(const YCHAR *path, int oflag, int mode)
+{
+ return yaffs_open_sharing(path, oflag, mode,
+ YAFFS_SHARE_READ | YAFFS_SHARE_WRITE);
+}
+
+int yaffs_Dofsync(int handle, int datasync)
+{
+ int retVal = -1;
+ struct yaffs_obj *obj;
+
+ yaffsfs_Lock();
+
+ obj = yaffsfs_HandleToObject(handle);
+
+ if (!obj)
+ yaffsfs_SetError(-EBADF);
+ else if (obj->my_dev->read_only)
+ yaffsfs_SetError(-EROFS);
+ else {
+ yaffs_flush_file(obj, 1, datasync);
+ retVal = 0;
+ }
+
+ yaffsfs_Unlock();
+
+ return retVal;
+}
+
+int yaffs_fsync(int handle)
+{
+ return yaffs_Dofsync(handle, 0);
+}
+
+int yaffs_flush(int handle)
+{
+ return yaffs_fsync(handle);
+}
+
+int yaffs_fdatasync(int handle)
+{
+ return yaffs_Dofsync(handle, 1);
+}
+
+int yaffs_close(int handle)
+{
+ struct yaffsfs_Handle *h = NULL;
+ struct yaffs_obj *obj = NULL;
+ int retVal = -1;
+
+ yaffsfs_Lock();
+
+ h = yaffsfs_HandleToPointer(handle);
+ obj = yaffsfs_HandleToObject(handle);
+
+ if (!h || !obj)
+ yaffsfs_SetError(-EBADF);
+ else {
+ /* clean up */
+ yaffs_flush_file(obj, 1, 0);
+ yaffsfs_PutHandle(handle);
+ retVal = 0;
+ }
+
+ yaffsfs_Unlock();
+
+ return retVal;
+}
+
+int yaffsfs_do_read(int handle, void *vbuf, unsigned int nbyte,
+ int isPread, loff_t offset)
+{
+ struct yaffsfs_FileDes *fd = NULL;
+ struct yaffs_obj *obj = NULL;
+ loff_t pos = 0;
+ loff_t startPos = 0;
+ loff_t endPos = 0;
+ int nRead = 0;
+ int nToRead = 0;
+ int totalRead = 0;
+ loff_t maxRead;
+ u8 *buf = (u8 *) vbuf;
+
+ if (!vbuf) {
+ yaffsfs_SetError(-EFAULT);
+ return -1;
+ }
+
+ yaffsfs_Lock();
+ fd = yaffsfs_HandleToFileDes(handle);
+ obj = yaffsfs_HandleToObject(handle);
+
+ if (!fd || !obj) {
+ /* bad handle */
+ yaffsfs_SetError(-EBADF);
+ totalRead = -1;
+ } else if (!fd->reading) {
+ /* Not a reading handle */
+ yaffsfs_SetError(-EINVAL);
+ totalRead = -1;
+ } else if (nbyte > YAFFS_MAX_FILE_SIZE) {
+ yaffsfs_SetError(-EINVAL);
+ totalRead = -1;
+ } else {
+ if (isPread)
+ startPos = offset;
+ else
+ startPos = fd->position;
+
+ pos = startPos;
+
+ if (yaffs_get_obj_length(obj) > pos)
+ maxRead = yaffs_get_obj_length(obj) - pos;
+ else
+ maxRead = 0;
+
+ if (nbyte > maxRead)
+ nbyte = maxRead;
+
+ yaffsfs_GetHandle(handle);
+
+ endPos = pos + nbyte;
+
+ if (pos < 0 || pos > YAFFS_MAX_FILE_SIZE ||
+ nbyte > YAFFS_MAX_FILE_SIZE ||
+ endPos < 0 || endPos > YAFFS_MAX_FILE_SIZE) {
+ totalRead = -1;
+ nbyte = 0;
+ }
+
+ while (nbyte > 0) {
+ nToRead = YAFFSFS_RW_SIZE -
+ (pos & (YAFFSFS_RW_SIZE - 1));
+ if (nToRead > nbyte)
+ nToRead = nbyte;
+
+ /* Tricky bit...
+ * Need to reverify object in case the device was
+ * unmounted in another thread.
+ */
+ obj = yaffsfs_HandleToObject(handle);
+ if (!obj)
+ nRead = 0;
+ else
+ nRead = yaffs_file_rd(obj, buf, pos, nToRead);
+
+ if (nRead > 0) {
+ totalRead += nRead;
+ pos += nRead;
+ buf += nRead;
+ }
+
+ if (nRead == nToRead)
+ nbyte -= nRead;
+ else
+ nbyte = 0; /* no more to read */
+
+ if (nbyte > 0) {
+ yaffsfs_Unlock();
+ yaffsfs_Lock();
+ }
+
+ }
+
+ yaffsfs_PutHandle(handle);
+
+ if (!isPread) {
+ if (totalRead >= 0)
+ fd->position = startPos + totalRead;
+ else
+ yaffsfs_SetError(-EINVAL);
+ }
+
+ }
+
+ yaffsfs_Unlock();
+
+ return (totalRead >= 0) ? totalRead : -1;
+
+}
+
+int yaffs_read(int handle, void *buf, unsigned int nbyte)
+{
+ return yaffsfs_do_read(handle, buf, nbyte, 0, 0);
+}
+
+int yaffs_pread(int handle, void *buf, unsigned int nbyte, loff_t offset)
+{
+ return yaffsfs_do_read(handle, buf, nbyte, 1, offset);
+}
+
+int yaffsfs_do_write(int handle, const void *vbuf, unsigned int nbyte,
+ int isPwrite, loff_t offset)
+{
+ struct yaffsfs_FileDes *fd = NULL;
+ struct yaffs_obj *obj = NULL;
+ loff_t pos = 0;
+ loff_t startPos = 0;
+ loff_t endPos;
+ int nWritten = 0;
+ int totalWritten = 0;
+ int write_trhrough = 0;
+ int nToWrite = 0;
+ const u8 *buf = (const u8 *)vbuf;
+
+ if (!vbuf) {
+ yaffsfs_SetError(-EFAULT);
+ return -1;
+ }
+
+ yaffsfs_Lock();
+ fd = yaffsfs_HandleToFileDes(handle);
+ obj = yaffsfs_HandleToObject(handle);
+
+ if (!fd || !obj) {
+ /* bad handle */
+ yaffsfs_SetError(-EBADF);
+ totalWritten = -1;
+ } else if (!fd->writing) {
+ yaffsfs_SetError(-EINVAL);
+ totalWritten = -1;
+ } else if (obj->my_dev->read_only) {
+ yaffsfs_SetError(-EROFS);
+ totalWritten = -1;
+ } else {
+ if (fd->append)
+ startPos = yaffs_get_obj_length(obj);
+ else if (isPwrite)
+ startPos = offset;
+ else
+ startPos = fd->position;
+
+ yaffsfs_GetHandle(handle);
+ pos = startPos;
+ endPos = pos + nbyte;
+
+ if (pos < 0 || pos > YAFFS_MAX_FILE_SIZE ||
+ nbyte > YAFFS_MAX_FILE_SIZE ||
+ endPos < 0 || endPos > YAFFS_MAX_FILE_SIZE) {
+ totalWritten = -1;
+ nbyte = 0;
+ }
+
+ while (nbyte > 0) {
+
+ nToWrite = YAFFSFS_RW_SIZE -
+ (pos & (YAFFSFS_RW_SIZE - 1));
+ if (nToWrite > nbyte)
+ nToWrite = nbyte;
+
+ /* Tricky bit...
+ * Need to reverify object in case the device was
+ * remounted or unmounted in another thread.
+ */
+ obj = yaffsfs_HandleToObject(handle);
+ if (!obj || obj->my_dev->read_only)
+ nWritten = 0;
+ else
+ nWritten =
+ yaffs_wr_file(obj, buf, pos, nToWrite,
+ write_trhrough);
+ if (nWritten > 0) {
+ totalWritten += nWritten;
+ pos += nWritten;
+ buf += nWritten;
+ }
+
+ if (nWritten == nToWrite)
+ nbyte -= nToWrite;
+ else
+ nbyte = 0;
+
+ if (nWritten < 1 && totalWritten < 1) {
+ yaffsfs_SetError(-ENOSPC);
+ totalWritten = -1;
+ }
+
+ if (nbyte > 0) {
+ yaffsfs_Unlock();
+ yaffsfs_Lock();
+ }
+ }
+
+ yaffsfs_PutHandle(handle);
+
+ if (!isPwrite) {
+ if (totalWritten > 0)
+ fd->position = startPos + totalWritten;
+ else
+ yaffsfs_SetError(-EINVAL);
+ }
+ }
+
+ yaffsfs_Unlock();
+
+ return (totalWritten >= 0) ? totalWritten : -1;
+}
+
+int yaffs_write(int fd, const void *buf, unsigned int nbyte)
+{
+ return yaffsfs_do_write(fd, buf, nbyte, 0, 0);
+}
+
+int yaffs_pwrite(int fd, const void *buf, unsigned int nbyte, loff_t offset)
+{
+ return yaffsfs_do_write(fd, buf, nbyte, 1, offset);
+}
+
+int yaffs_truncate(const YCHAR *path, loff_t new_size)
+{
+ struct yaffs_obj *obj = NULL;
+ struct yaffs_obj *dir = NULL;
+ int result = YAFFS_FAIL;
+ int notDir = 0;
+ int loop = 0;
+
+ if (!path) {
+ yaffsfs_SetError(-EFAULT);
+ return -1;
+ }
+
+ if (yaffsfs_CheckPath(path) < 0) {
+ yaffsfs_SetError(-ENAMETOOLONG);
+ return -1;
+ }
+
+ yaffsfs_Lock();
+
+ obj = yaffsfs_FindObject(NULL, path, 0, 1, &dir, &notDir, &loop);
+ obj = yaffsfs_FollowLink(obj, 0, &loop);
+
+ if (!dir && notDir)
+ yaffsfs_SetError(-ENOTDIR);
+ else if (loop)
+ yaffsfs_SetError(-ELOOP);
+ else if (!dir || !obj)
+ yaffsfs_SetError(-ENOENT);
+ else if (obj->my_dev->read_only)
+ yaffsfs_SetError(-EROFS);
+ else if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE)
+ yaffsfs_SetError(-EISDIR);
+ else if (obj->my_dev->read_only)
+ yaffsfs_SetError(-EROFS);
+ else if (new_size < 0 || new_size > YAFFS_MAX_FILE_SIZE)
+ yaffsfs_SetError(-EINVAL);
+ else
+ result = yaffs_resize_file(obj, new_size);
+
+ yaffsfs_Unlock();
+
+ return (result) ? 0 : -1;
+}
+
+int yaffs_ftruncate(int handle, loff_t new_size)
+{
+ struct yaffsfs_FileDes *fd = NULL;
+ struct yaffs_obj *obj = NULL;
+ int result = 0;
+
+ yaffsfs_Lock();
+ fd = yaffsfs_HandleToFileDes(handle);
+ obj = yaffsfs_HandleToObject(handle);
+
+ if (!fd || !obj)
+ /* bad handle */
+ yaffsfs_SetError(-EBADF);
+ else if (!fd->writing)
+ yaffsfs_SetError(-EINVAL);
+ else if (obj->my_dev->read_only)
+ yaffsfs_SetError(-EROFS);
+ else if (new_size < 0 || new_size > YAFFS_MAX_FILE_SIZE)
+ yaffsfs_SetError(-EINVAL);
+ else
+ /* resize the file */
+ result = yaffs_resize_file(obj, new_size);
+ yaffsfs_Unlock();
+
+ return (result) ? 0 : -1;
+
+}
+
+loff_t yaffs_lseek(int handle, loff_t offset, int whence)
+{
+ struct yaffsfs_FileDes *fd = NULL;
+ struct yaffs_obj *obj = NULL;
+ loff_t pos = -1;
+ loff_t fSize = -1;
+
+ yaffsfs_Lock();
+ fd = yaffsfs_HandleToFileDes(handle);
+ obj = yaffsfs_HandleToObject(handle);
+
+ if (!fd || !obj)
+ yaffsfs_SetError(-EBADF);
+ else if (offset > YAFFS_MAX_FILE_SIZE)
+ yaffsfs_SetError(-EINVAL);
+ else {
+ if (whence == SEEK_SET) {
+ if (offset >= 0)
+ pos = offset;
+ } else if (whence == SEEK_CUR) {
+ if ((fd->position + offset) >= 0)
+ pos = (fd->position + offset);
+ } else if (whence == SEEK_END) {
+ fSize = yaffs_get_obj_length(obj);
+ if (fSize >= 0 && (fSize + offset) >= 0)
+ pos = fSize + offset;
+ }
+
+ if (pos >= 0 && pos <= YAFFS_MAX_FILE_SIZE)
+ fd->position = pos;
+ else {
+ yaffsfs_SetError(-EINVAL);
+ pos = -1;
+ }
+ }
+
+ yaffsfs_Unlock();
+
+ return pos;
+}
+
+int yaffsfs_DoUnlink(const YCHAR *path, int isDirectory)
+{
+ struct yaffs_obj *dir = NULL;
+ struct yaffs_obj *obj = NULL;
+ YCHAR *name;
+ int result = YAFFS_FAIL;
+ int notDir = 0;
+ int loop = 0;
+
+ if (!path) {
+ yaffsfs_SetError(-EFAULT);
+ return -1;
+ }
+
+ if (yaffsfs_CheckPath(path) < 0) {
+ yaffsfs_SetError(-ENAMETOOLONG);
+ return -1;
+ }
+
+ yaffsfs_Lock();
+
+ obj = yaffsfs_FindObject(NULL, path, 0, 0, NULL, NULL, NULL);
+ dir = yaffsfs_FindDirectory(NULL, path, &name, 0, &notDir, &loop);
+
+ if (!dir && notDir)
+ yaffsfs_SetError(-ENOTDIR);
+ else if (loop)
+ yaffsfs_SetError(-ELOOP);
+ else if (!dir)
+ yaffsfs_SetError(-ENOENT);
+ else if (yaffs_strncmp(name, _Y("."), 2) == 0)
+ yaffsfs_SetError(-EINVAL);
+ else if (!obj)
+ yaffsfs_SetError(-ENOENT);
+ else if (obj->my_dev->read_only)
+ yaffsfs_SetError(-EROFS);
+ else if (!isDirectory &&
+ obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY)
+ yaffsfs_SetError(-EISDIR);
+ else if (isDirectory &&
+ obj->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY)
+ yaffsfs_SetError(-ENOTDIR);
+ else if (isDirectory && obj == obj->my_dev->root_dir)
+ yaffsfs_SetError(-EBUSY); /* Can't rmdir a root */
+ else {
+ result = yaffs_unlinker(dir, name);
+
+ if (result == YAFFS_FAIL && isDirectory)
+ yaffsfs_SetError(-ENOTEMPTY);
+ }
+
+ yaffsfs_Unlock();
+
+ return (result == YAFFS_FAIL) ? -1 : 0;
+}
+
+int yaffs_unlink(const YCHAR *path)
+{
+ return yaffsfs_DoUnlink(path, 0);
+}
+
+int yaffs_rename(const YCHAR *oldPath, const YCHAR *newPath)
+{
+ struct yaffs_obj *olddir = NULL;
+ struct yaffs_obj *newdir = NULL;
+ struct yaffs_obj *obj = NULL;
+ struct yaffs_obj *newobj = NULL;
+ YCHAR *oldname;
+ YCHAR *newname;
+ int result = YAFFS_FAIL;
+ int rename_allowed = 1;
+ int notOldDir = 0;
+ int notNewDir = 0;
+ int oldLoop = 0;
+ int newLoop = 0;
+
+ YCHAR *alt_newpath = NULL;
+
+ if (!oldPath || !newPath) {
+ yaffsfs_SetError(-EFAULT);
+ return -1;
+ }
+
+ if (yaffsfs_CheckPath(oldPath) < 0 || yaffsfs_CheckPath(newPath) < 0) {
+ yaffsfs_SetError(-ENAMETOOLONG);
+ return -1;
+ }
+
+ if (yaffsfs_alt_dir_path(newPath, &alt_newpath) < 0) {
+ yaffsfs_SetError(-ENOMEM);
+ return -1;
+ }
+ if (alt_newpath)
+ newPath = alt_newpath;
+
+ yaffsfs_Lock();
+
+ olddir = yaffsfs_FindDirectory(NULL, oldPath, &oldname, 0,
+ &notOldDir, &oldLoop);
+ newdir = yaffsfs_FindDirectory(NULL, newPath, &newname, 0,
+ &notNewDir, &newLoop);
+ obj = yaffsfs_FindObject(NULL, oldPath, 0, 0, NULL, NULL, NULL);
+ newobj = yaffsfs_FindObject(NULL, newPath, 0, 0, NULL, NULL, NULL);
+
+ /* If the object being renamed is a directory and the
+ * path ended with a "/" then the olddir == obj.
+ * We pass through NULL for the old name to tell the lower layers
+ * to use olddir as the object.
+ */
+
+ if (olddir == obj)
+ oldname = NULL;
+
+ if ((!olddir && notOldDir) || (!newdir && notNewDir)) {
+ yaffsfs_SetError(-ENOTDIR);
+ rename_allowed = 0;
+ } else if (oldLoop || newLoop) {
+ yaffsfs_SetError(-ELOOP);
+ rename_allowed = 0;
+ } else if (olddir && oldname &&
+ yaffs_strncmp(oldname, _Y("."), 2) == 0) {
+ yaffsfs_SetError(-EINVAL);
+ rename_allowed = 0;
+ } else if (!olddir || !newdir || !obj) {
+ yaffsfs_SetError(-ENOENT);
+ rename_allowed = 0;
+ } else if (obj->my_dev->read_only) {
+ yaffsfs_SetError(-EROFS);
+ rename_allowed = 0;
+ } else if (yaffs_is_non_empty_dir(newobj)) {
+ yaffsfs_SetError(-ENOTEMPTY);
+ rename_allowed = 0;
+ } else if (olddir->my_dev != newdir->my_dev) {
+ /* Rename must be on same device */
+ yaffsfs_SetError(-EXDEV);
+ rename_allowed = 0;
+ } else if (obj && obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) {
+ /*
+ * It is a directory, check that it is not being renamed to
+ * being its own decendent.
+ * Do this by tracing from the new directory back to the root,
+ * checking for obj
+ */
+
+ struct yaffs_obj *xx = newdir;
+
+ while (rename_allowed && xx) {
+ if (xx == obj)
+ rename_allowed = 0;
+ xx = xx->parent;
+ }
+ if (!rename_allowed)
+ yaffsfs_SetError(-EINVAL);
+ }
+
+ if (rename_allowed)
+ result = yaffs_rename_obj(olddir, oldname, newdir, newname);
+
+ yaffsfs_Unlock();
+
+ kfree(alt_newpath);
+
+ return (result == YAFFS_FAIL) ? -1 : 0;
+}
+
+static int yaffsfs_DoStat(struct yaffs_obj *obj, struct yaffs_stat *buf)
+{
+ int retVal = -1;
+
+ obj = yaffs_get_equivalent_obj(obj);
+
+ if (obj && buf) {
+ buf->st_dev = (int)obj->my_dev->os_context;
+ buf->st_ino = obj->obj_id;
+ buf->st_mode = obj->yst_mode & ~S_IFMT;
+
+ if (obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY)
+ buf->st_mode |= S_IFDIR;
+ else if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK)
+ buf->st_mode |= S_IFLNK;
+ else if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE)
+ buf->st_mode |= S_IFREG;
+
+ buf->st_nlink = yaffs_get_obj_link_count(obj);
+ buf->st_uid = 0;
+ buf->st_gid = 0;
+ buf->st_rdev = obj->yst_rdev;
+ buf->st_size = yaffs_get_obj_length(obj);
+ buf->st_blksize = obj->my_dev->data_bytes_per_chunk;
+ buf->st_blocks = lldiv(buf->st_size + buf->st_blksize - 1,
+ buf->st_blksize);
+#if CONFIG_YAFFS_WINCE
+ buf->yst_wince_atime[0] = obj->win_atime[0];
+ buf->yst_wince_atime[1] = obj->win_atime[1];
+ buf->yst_wince_ctime[0] = obj->win_ctime[0];
+ buf->yst_wince_ctime[1] = obj->win_ctime[1];
+ buf->yst_wince_mtime[0] = obj->win_mtime[0];
+ buf->yst_wince_mtime[1] = obj->win_mtime[1];
+#else
+ buf->yst_atime = obj->yst_atime;
+ buf->yst_ctime = obj->yst_ctime;
+ buf->yst_mtime = obj->yst_mtime;
+#endif
+ retVal = 0;
+ }
+ return retVal;
+}
+
+static int yaffsfs_DoStatOrLStat(const YCHAR *path,
+ struct yaffs_stat *buf, int doLStat)
+{
+ struct yaffs_obj *obj = NULL;
+ struct yaffs_obj *dir = NULL;
+ int retVal = -1;
+ int notDir = 0;
+ int loop = 0;
+
+ if (!path || !buf) {
+ yaffsfs_SetError(-EFAULT);
+ return -1;
+ }
+
+ if (yaffsfs_CheckPath(path) < 0) {
+ yaffsfs_SetError(-ENAMETOOLONG);
+ return -1;
+ }
+
+ yaffsfs_Lock();
+
+ obj = yaffsfs_FindObject(NULL, path, 0, 1, &dir, &notDir, &loop);
+
+ if (!doLStat && obj)
+ obj = yaffsfs_FollowLink(obj, 0, &loop);
+
+ if (!dir && notDir)
+ yaffsfs_SetError(-ENOTDIR);
+ else if (loop)
+ yaffsfs_SetError(-ELOOP);
+ else if (!dir || !obj)
+ yaffsfs_SetError(-ENOENT);
+ else
+ retVal = yaffsfs_DoStat(obj, buf);
+
+ yaffsfs_Unlock();
+
+ return retVal;
+
+}
+
+int yaffs_stat(const YCHAR *path, struct yaffs_stat *buf)
+{
+ return yaffsfs_DoStatOrLStat(path, buf, 0);
+}
+
+int yaffs_lstat(const YCHAR *path, struct yaffs_stat *buf)
+{
+ return yaffsfs_DoStatOrLStat(path, buf, 1);
+}
+
+int yaffs_fstat(int fd, struct yaffs_stat *buf)
+{
+ struct yaffs_obj *obj;
+
+ int retVal = -1;
+
+ if (!buf) {
+ yaffsfs_SetError(-EFAULT);
+ return -1;
+ }
+
+ yaffsfs_Lock();
+ obj = yaffsfs_HandleToObject(fd);
+
+ if (obj)
+ retVal = yaffsfs_DoStat(obj, buf);
+ else
+ /* bad handle */
+ yaffsfs_SetError(-EBADF);
+
+ yaffsfs_Unlock();
+
+ return retVal;
+}
+
+static int yaffsfs_DoUtime(struct yaffs_obj *obj,
+ const struct yaffs_utimbuf *buf)
+{
+ int retVal = -1;
+ int result;
+
+ struct yaffs_utimbuf local;
+
+ obj = yaffs_get_equivalent_obj(obj);
+
+ if (obj && obj->my_dev->read_only) {
+ yaffsfs_SetError(-EROFS);
+ return -1;
+ }
+
+ if (!buf) {
+ local.actime = Y_CURRENT_TIME;
+ local.modtime = local.actime;
+ buf = &local;
+ }
+
+ if (obj) {
+ obj->yst_atime = buf->actime;
+ obj->yst_mtime = buf->modtime;
+ obj->dirty = 1;
+ result = yaffs_flush_file(obj, 0, 0);
+ retVal = result == YAFFS_OK ? 0 : -1;
+ }
+
+ return retVal;
+}
+
+int yaffs_utime(const YCHAR *path, const struct yaffs_utimbuf *buf)
+{
+ struct yaffs_obj *obj = NULL;
+ struct yaffs_obj *dir = NULL;
+ int retVal = -1;
+ int notDir = 0;
+ int loop = 0;
+
+ if (!path) {
+ yaffsfs_SetError(-EFAULT);
+ return -1;
+ }
+
+ if (yaffsfs_CheckPath(path) < 0) {
+ yaffsfs_SetError(-ENAMETOOLONG);
+ return -1;
+ }
+
+ yaffsfs_Lock();
+
+ obj = yaffsfs_FindObject(NULL, path, 0, 1, &dir, &notDir, &loop);
+
+ if (!dir && notDir)
+ yaffsfs_SetError(-ENOTDIR);
+ else if (loop)
+ yaffsfs_SetError(-ELOOP);
+ else if (!dir || !obj)
+ yaffsfs_SetError(-ENOENT);
+ else
+ retVal = yaffsfs_DoUtime(obj, buf);
+
+ yaffsfs_Unlock();
+
+ return retVal;
+
+}
+
+int yaffs_futime(int fd, const struct yaffs_utimbuf *buf)
+{
+ struct yaffs_obj *obj;
+
+ int retVal = -1;
+
+ yaffsfs_Lock();
+ obj = yaffsfs_HandleToObject(fd);
+
+ if (obj)
+ retVal = yaffsfs_DoUtime(obj, buf);
+ else
+ /* bad handle */
+ yaffsfs_SetError(-EBADF);
+
+ yaffsfs_Unlock();
+
+ return retVal;
+}
+
+#ifndef CONFIG_YAFFS_WINCE
+/* xattrib functions */
+
+static int yaffs_do_setxattr(const YCHAR *path, const char *name,
+ const void *data, int size, int flags, int follow)
+{
+ struct yaffs_obj *obj;
+ struct yaffs_obj *dir;
+ int notDir = 0;
+ int loop = 0;
+
+ int retVal = -1;
+
+ if (!path || !name || !data) {
+ yaffsfs_SetError(-EFAULT);
+ return -1;
+ }
+
+ if (yaffsfs_CheckPath(path) < 0) {
+ yaffsfs_SetError(-ENAMETOOLONG);
+ return -1;
+ }
+
+ yaffsfs_Lock();
+
+ obj = yaffsfs_FindObject(NULL, path, 0, 1, &dir, &notDir, &loop);
+
+ if (follow)
+ obj = yaffsfs_FollowLink(obj, 0, &loop);
+
+ if (!dir && notDir)
+ yaffsfs_SetError(-ENOTDIR);
+ else if (loop)
+ yaffsfs_SetError(-ELOOP);
+ else if (!dir || !obj)
+ yaffsfs_SetError(-ENOENT);
+ else {
+ retVal = yaffs_set_xattrib(obj, name, data, size, flags);
+ if (retVal < 0) {
+ yaffsfs_SetError(retVal);
+ retVal = -1;
+ }
+ }
+
+ yaffsfs_Unlock();
+
+ return retVal;
+
+}
+
+int yaffs_setxattr(const YCHAR *path, const char *name,
+ const void *data, int size, int flags)
+{
+ return yaffs_do_setxattr(path, name, data, size, flags, 1);
+}
+
+int yaffs_lsetxattr(const YCHAR *path, const char *name,
+ const void *data, int size, int flags)
+{
+ return yaffs_do_setxattr(path, name, data, size, flags, 0);
+}
+
+int yaffs_fsetxattr(int fd, const char *name,
+ const void *data, int size, int flags)
+{
+ struct yaffs_obj *obj;
+
+ int retVal = -1;
+
+ if (!name || !data) {
+ yaffsfs_SetError(-EFAULT);
+ return -1;
+ }
+
+ yaffsfs_Lock();
+ obj = yaffsfs_HandleToObject(fd);
+
+ if (!obj)
+ yaffsfs_SetError(-EBADF);
+ else {
+ retVal = yaffs_set_xattrib(obj, name, data, size, flags);
+ if (retVal < 0) {
+ yaffsfs_SetError(retVal);
+ retVal = -1;
+ }
+ }
+
+ yaffsfs_Unlock();
+
+ return retVal;
+}
+
+static int yaffs_do_getxattr(const YCHAR *path, const char *name,
+ void *data, int size, int follow)
+{
+ struct yaffs_obj *obj;
+ struct yaffs_obj *dir;
+ int retVal = -1;
+ int notDir = 0;
+ int loop = 0;
+
+ if (!path || !name || !data) {
+ yaffsfs_SetError(-EFAULT);
+ return -1;
+ }
+
+ if (yaffsfs_CheckPath(path) < 0) {
+ yaffsfs_SetError(-ENAMETOOLONG);
+ return -1;
+ }
+
+ yaffsfs_Lock();
+
+ obj = yaffsfs_FindObject(NULL, path, 0, 1, &dir, &notDir, &loop);
+
+ if (follow)
+ obj = yaffsfs_FollowLink(obj, 0, &loop);
+
+ if (!dir && notDir)
+ yaffsfs_SetError(-ENOTDIR);
+ else if (loop)
+ yaffsfs_SetError(-ELOOP);
+ else if (!dir || !obj)
+ yaffsfs_SetError(-ENOENT);
+ else {
+ retVal = yaffs_get_xattrib(obj, name, data, size);
+ if (retVal < 0) {
+ yaffsfs_SetError(retVal);
+ retVal = -1;
+ }
+ }
+ yaffsfs_Unlock();
+
+ return retVal;
+
+}
+
+int yaffs_getxattr(const YCHAR *path, const char *name, void *data, int size)
+{
+ return yaffs_do_getxattr(path, name, data, size, 1);
+}
+
+int yaffs_lgetxattr(const YCHAR *path, const char *name, void *data, int size)
+{
+ return yaffs_do_getxattr(path, name, data, size, 0);
+}
+
+int yaffs_fgetxattr(int fd, const char *name, void *data, int size)
+{
+ struct yaffs_obj *obj;
+
+ int retVal = -1;
+
+ if (!name || !data) {
+ yaffsfs_SetError(-EFAULT);
+ return -1;
+ }
+
+ yaffsfs_Lock();
+ obj = yaffsfs_HandleToObject(fd);
+
+ if (obj) {
+ retVal = yaffs_get_xattrib(obj, name, data, size);
+ if (retVal < 0) {
+ yaffsfs_SetError(retVal);
+ retVal = -1;
+ }
+ } else
+ /* bad handle */
+ yaffsfs_SetError(-EBADF);
+
+ yaffsfs_Unlock();
+
+ return retVal;
+}
+
+static int yaffs_do_listxattr(const YCHAR *path, char *data,
+ int size, int follow)
+{
+ struct yaffs_obj *obj = NULL;
+ struct yaffs_obj *dir = NULL;
+ int retVal = -1;
+ int notDir = 0;
+ int loop = 0;
+
+ if (!path || !data) {
+ yaffsfs_SetError(-EFAULT);
+ return -1;
+ }
+
+ if (yaffsfs_CheckPath(path) < 0) {
+ yaffsfs_SetError(-ENAMETOOLONG);
+ return -1;
+ }
+
+ yaffsfs_Lock();
+
+ obj = yaffsfs_FindObject(NULL, path, 0, 1, &dir, &notDir, &loop);
+
+ if (follow)
+ obj = yaffsfs_FollowLink(obj, 0, &loop);
+
+ if (!dir && notDir)
+ yaffsfs_SetError(-ENOTDIR);
+ else if (loop)
+ yaffsfs_SetError(-ELOOP);
+ else if (!dir || !obj)
+ yaffsfs_SetError(-ENOENT);
+ else {
+ retVal = yaffs_list_xattrib(obj, data, size);
+ if (retVal < 0) {
+ yaffsfs_SetError(retVal);
+ retVal = -1;
+ }
+ }
+
+ yaffsfs_Unlock();
+
+ return retVal;
+
+}
+
+int yaffs_listxattr(const YCHAR *path, char *data, int size)
+{
+ return yaffs_do_listxattr(path, data, size, 1);
+}
+
+int yaffs_llistxattr(const YCHAR *path, char *data, int size)
+{
+ return yaffs_do_listxattr(path, data, size, 0);
+}
+
+int yaffs_flistxattr(int fd, char *data, int size)
+{
+ struct yaffs_obj *obj;
+
+ int retVal = -1;
+
+ if (!data) {
+ yaffsfs_SetError(-EFAULT);
+ return -1;
+ }
+
+ yaffsfs_Lock();
+ obj = yaffsfs_HandleToObject(fd);
+
+ if (obj) {
+ retVal = yaffs_list_xattrib(obj, data, size);
+ if (retVal < 0) {
+ yaffsfs_SetError(retVal);
+ retVal = -1;
+ }
+ } else
+ /* bad handle */
+ yaffsfs_SetError(-EBADF);
+
+ yaffsfs_Unlock();
+
+ return retVal;
+}
+
+static int yaffs_do_removexattr(const YCHAR *path, const char *name,
+ int follow)
+{
+ struct yaffs_obj *obj = NULL;
+ struct yaffs_obj *dir = NULL;
+ int notDir = 0;
+ int loop = 0;
+ int retVal = -1;
+
+ if (!path || !name) {
+ yaffsfs_SetError(-EFAULT);
+ return -1;
+ }
+
+ if (yaffsfs_CheckPath(path) < 0) {
+ yaffsfs_SetError(-ENAMETOOLONG);
+ return -1;
+ }
+
+ yaffsfs_Lock();
+
+ obj = yaffsfs_FindObject(NULL, path, 0, 1, &dir, &notDir, &loop);
+
+ if (follow)
+ obj = yaffsfs_FollowLink(obj, 0, &loop);
+
+ if (!dir && notDir)
+ yaffsfs_SetError(-ENOTDIR);
+ else if (loop)
+ yaffsfs_SetError(-ELOOP);
+ else if (!dir || !obj)
+ yaffsfs_SetError(-ENOENT);
+ else {
+ retVal = yaffs_remove_xattrib(obj, name);
+ if (retVal < 0) {
+ yaffsfs_SetError(retVal);
+ retVal = -1;
+ }
+ }
+
+ yaffsfs_Unlock();
+
+ return retVal;
+
+}
+
+int yaffs_removexattr(const YCHAR *path, const char *name)
+{
+ return yaffs_do_removexattr(path, name, 1);
+}
+
+int yaffs_lremovexattr(const YCHAR *path, const char *name)
+{
+ return yaffs_do_removexattr(path, name, 0);
+}
+
+int yaffs_fremovexattr(int fd, const char *name)
+{
+ struct yaffs_obj *obj;
+
+ int retVal = -1;
+
+ if (!name) {
+ yaffsfs_SetError(-EFAULT);
+ return -1;
+ }
+
+ yaffsfs_Lock();
+ obj = yaffsfs_HandleToObject(fd);
+
+ if (obj) {
+ retVal = yaffs_remove_xattrib(obj, name);
+ if (retVal < 0) {
+ yaffsfs_SetError(retVal);
+ retVal = -1;
+ }
+ } else
+ /* bad handle */
+ yaffsfs_SetError(-EBADF);
+
+ yaffsfs_Unlock();
+
+ return retVal;
+}
+#endif
+
+#ifdef CONFIG_YAFFS_WINCE
+int yaffs_get_wince_times(int fd, unsigned *wctime,
+ unsigned *watime, unsigned *wmtime)
+{
+ struct yaffs_obj *obj;
+
+ int retVal = -1;
+
+ yaffsfs_Lock();
+ obj = yaffsfs_HandleToObject(fd);
+
+ if (obj) {
+
+ if (wctime) {
+ wctime[0] = obj->win_ctime[0];
+ wctime[1] = obj->win_ctime[1];
+ }
+ if (watime) {
+ watime[0] = obj->win_atime[0];
+ watime[1] = obj->win_atime[1];
+ }
+ if (wmtime) {
+ wmtime[0] = obj->win_mtime[0];
+ wmtime[1] = obj->win_mtime[1];
+ }
+
+ retVal = 0;
+ } else
+ /* bad handle */
+ yaffsfs_SetError(-EBADF);
+
+ yaffsfs_Unlock();
+
+ return retVal;
+}
+
+int yaffs_set_wince_times(int fd,
+ const unsigned *wctime,
+ const unsigned *watime, const unsigned *wmtime)
+{
+ struct yaffs_obj *obj;
+ int result;
+ int retVal = -1;
+
+ yaffsfs_Lock();
+ obj = yaffsfs_HandleToObject(fd);
+
+ if (obj) {
+
+ if (wctime) {
+ obj->win_ctime[0] = wctime[0];
+ obj->win_ctime[1] = wctime[1];
+ }
+ if (watime) {
+ obj->win_atime[0] = watime[0];
+ obj->win_atime[1] = watime[1];
+ }
+ if (wmtime) {
+ obj->win_mtime[0] = wmtime[0];
+ obj->win_mtime[1] = wmtime[1];
+ }
+
+ obj->dirty = 1;
+ result = yaffs_flush_file(obj, 0, 0);
+ retVal = 0;
+ } else
+ /* bad handle */
+ yaffsfs_SetError(-EBADF);
+
+ yaffsfs_Unlock();
+
+ return retVal;
+}
+
+#endif
+
+static int yaffsfs_DoChMod(struct yaffs_obj *obj, mode_t mode)
+{
+ int result = -1;
+
+ if (obj)
+ obj = yaffs_get_equivalent_obj(obj);
+
+ if (obj) {
+ obj->yst_mode = mode;
+ obj->dirty = 1;
+ result = yaffs_flush_file(obj, 0, 0);
+ }
+
+ return result == YAFFS_OK ? 0 : -1;
+}
+
+int yaffs_access(const YCHAR *path, int amode)
+{
+ struct yaffs_obj *obj = NULL;
+ struct yaffs_obj *dir = NULL;
+ int notDir = 0;
+ int loop = 0;
+ int retval = -1;
+
+ if (!path) {
+ yaffsfs_SetError(-EFAULT);
+ return -1;
+ }
+
+ if (yaffsfs_CheckPath(path) < 0) {
+ yaffsfs_SetError(-ENAMETOOLONG);
+ return -1;
+ }
+
+ if (amode & ~(R_OK | W_OK | X_OK)) {
+ yaffsfs_SetError(-EINVAL);
+ return -1;
+ }
+
+ yaffsfs_Lock();
+
+ obj = yaffsfs_FindObject(NULL, path, 0, 1, &dir, &notDir, &loop);
+ obj = yaffsfs_FollowLink(obj, 0, &loop);
+
+ if (!dir && notDir)
+ yaffsfs_SetError(-ENOTDIR);
+ else if (loop)
+ yaffsfs_SetError(-ELOOP);
+ else if (!dir || !obj)
+ yaffsfs_SetError(-ENOENT);
+ else if ((amode & W_OK) && obj->my_dev->read_only)
+ yaffsfs_SetError(-EROFS);
+ else {
+ int access_ok = 1;
+
+ if ((amode & R_OK) && !(obj->yst_mode & S_IREAD))
+ access_ok = 0;
+ if ((amode & W_OK) && !(obj->yst_mode & S_IWRITE))
+ access_ok = 0;
+ if ((amode & X_OK) && !(obj->yst_mode & S_IEXEC))
+ access_ok = 0;
+
+ if (!access_ok)
+ yaffsfs_SetError(-EACCES);
+ else
+ retval = 0;
+ }
+
+ yaffsfs_Unlock();
+
+ return retval;
+
+}
+
+int yaffs_chmod(const YCHAR *path, mode_t mode)
+{
+ struct yaffs_obj *obj = NULL;
+ struct yaffs_obj *dir = NULL;
+ int retVal = -1;
+ int notDir = 0;
+ int loop = 0;
+
+ if (!path) {
+ yaffsfs_SetError(-EFAULT);
+ return -1;
+ }
+
+ if (yaffsfs_CheckPath(path) < 0) {
+ yaffsfs_SetError(-ENAMETOOLONG);
+ return -1;
+ }
+
+ if (mode & ~(0777)) {
+ yaffsfs_SetError(-EINVAL);
+ return -1;
+ }
+
+ yaffsfs_Lock();
+
+ obj = yaffsfs_FindObject(NULL, path, 0, 1, &dir, &notDir, &loop);
+ obj = yaffsfs_FollowLink(obj, 0, &loop);
+
+ if (!dir && notDir)
+ yaffsfs_SetError(-ENOTDIR);
+ else if (loop)
+ yaffsfs_SetError(-ELOOP);
+ else if (!dir || !obj)
+ yaffsfs_SetError(-ENOENT);
+ else if (obj->my_dev->read_only)
+ yaffsfs_SetError(-EROFS);
+ else
+ retVal = yaffsfs_DoChMod(obj, mode);
+
+ yaffsfs_Unlock();
+
+ return retVal;
+
+}
+
+int yaffs_fchmod(int fd, mode_t mode)
+{
+ struct yaffs_obj *obj;
+ int retVal = -1;
+
+ if (mode & ~(0777)) {
+ yaffsfs_SetError(-EINVAL);
+ return -1;
+ }
+
+ yaffsfs_Lock();
+ obj = yaffsfs_HandleToObject(fd);
+
+ if (!obj)
+ yaffsfs_SetError(-EBADF);
+ else if (obj->my_dev->read_only)
+ yaffsfs_SetError(-EROFS);
+ else
+ retVal = yaffsfs_DoChMod(obj, mode);
+
+ yaffsfs_Unlock();
+
+ return retVal;
+}
+
+int yaffs_mkdir(const YCHAR *path, mode_t mode)
+{
+ struct yaffs_obj *parent = NULL;
+ struct yaffs_obj *dir = NULL;
+ YCHAR *name;
+ YCHAR *alt_path = NULL;
+ int retVal = -1;
+ int notDir = 0;
+ int loop = 0;
+
+ if (!path) {
+ yaffsfs_SetError(-EFAULT);
+ return -1;
+ }
+
+ if (yaffsfs_CheckPath(path) < 0) {
+ yaffsfs_SetError(-ENAMETOOLONG);
+ return -1;
+ }
+
+ if (yaffsfs_alt_dir_path(path, &alt_path) < 0) {
+ yaffsfs_SetError(-ENOMEM);
+ return -1;
+ }
+ if (alt_path)
+ path = alt_path;
+
+ yaffsfs_Lock();
+ parent = yaffsfs_FindDirectory(NULL, path, &name, 0, &notDir, &loop);
+ if (!parent && notDir)
+ yaffsfs_SetError(-ENOTDIR);
+ else if (loop)
+ yaffsfs_SetError(-ELOOP);
+ else if (!parent)
+ yaffsfs_SetError(-ENOENT);
+ else if (yaffsfs_TooManyObjects(parent->my_dev))
+ yaffsfs_SetError(-ENFILE);
+ else if (yaffs_strnlen(name, 5) == 0) {
+ /* Trying to make the root itself */
+ yaffsfs_SetError(-EEXIST);
+ } else if (parent->my_dev->read_only)
+ yaffsfs_SetError(-EROFS);
+ else {
+ dir = yaffs_create_dir(parent, name, mode, 0, 0);
+ if (dir)
+ retVal = 0;
+ else if (yaffs_find_by_name(parent, name))
+ yaffsfs_SetError(-EEXIST); /* name exists */
+ else
+ yaffsfs_SetError(-ENOSPC); /* assume no space */
+ }
+
+ yaffsfs_Unlock();
+
+ kfree(alt_path);
+
+ return retVal;
+}
+
+int yaffs_rmdir(const YCHAR *path)
+{
+ int result;
+ YCHAR *alt_path;
+
+ if (!path) {
+ yaffsfs_SetError(-EFAULT);
+ return -1;
+ }
+
+ if (yaffsfs_CheckPath(path) < 0) {
+ yaffsfs_SetError(-ENAMETOOLONG);
+ return -1;
+ }
+
+ if (yaffsfs_alt_dir_path(path, &alt_path) < 0) {
+ yaffsfs_SetError(-ENOMEM);
+ return -1;
+ }
+ if (alt_path)
+ path = alt_path;
+ result = yaffsfs_DoUnlink(path, 1);
+
+ kfree(alt_path);
+
+ return result;
+}
+
+void *yaffs_getdev(const YCHAR *path)
+{
+ struct yaffs_dev *dev = NULL;
+ YCHAR *dummy;
+ dev = yaffsfs_FindDevice(path, &dummy);
+ return (void *)dev;
+}
+
+int yaffs_mount_common(const YCHAR *path, int read_only, int skip_checkpt)
+{
+ int retVal = -1;
+ int result = YAFFS_FAIL;
+ struct yaffs_dev *dev = NULL;
+
+ if (!path) {
+ yaffsfs_SetError(-EFAULT);
+ return -1;
+ }
+
+ yaffs_trace(YAFFS_TRACE_MOUNT, "yaffs: Mounting %s", path);
+
+ if (yaffsfs_CheckPath(path) < 0) {
+ yaffsfs_SetError(-ENAMETOOLONG);
+ return -1;
+ }
+
+ yaffsfs_Lock();
+
+ yaffsfs_InitHandles();
+
+ dev = yaffsfs_FindMountPoint(path);
+ if (dev) {
+ if (!dev->is_mounted) {
+ dev->read_only = read_only ? 1 : 0;
+ if (skip_checkpt) {
+ u8 skip = dev->param.skip_checkpt_rd;
+ dev->param.skip_checkpt_rd = 1;
+ result = yaffs_guts_initialise(dev);
+ dev->param.skip_checkpt_rd = skip;
+ } else {
+ result = yaffs_guts_initialise(dev);
+ }
+
+ if (result == YAFFS_FAIL)
+ yaffsfs_SetError(-ENOMEM);
+ retVal = result ? 0 : -1;
+
+ } else
+ yaffsfs_SetError(-EBUSY);
+ } else
+ yaffsfs_SetError(-ENODEV);
+
+ yaffsfs_Unlock();
+ return retVal;
+
+}
+
+int yaffs_mount2(const YCHAR *path, int readonly)
+{
+ return yaffs_mount_common(path, readonly, 0);
+}
+
+int yaffs_mount(const YCHAR *path)
+{
+ return yaffs_mount_common(path, 0, 0);
+}
+
+int yaffs_sync(const YCHAR *path)
+{
+ int retVal = -1;
+ struct yaffs_dev *dev = NULL;
+ YCHAR *dummy;
+
+ if (!path) {
+ yaffsfs_SetError(-EFAULT);
+ return -1;
+ }
+
+ if (yaffsfs_CheckPath(path) < 0) {
+ yaffsfs_SetError(-ENAMETOOLONG);
+ return -1;
+ }
+
+ yaffsfs_Lock();
+ dev = yaffsfs_FindDevice(path, &dummy);
+ if (dev) {
+ if (!dev->is_mounted)
+ yaffsfs_SetError(-EINVAL);
+ else if (dev->read_only)
+ yaffsfs_SetError(-EROFS);
+ else {
+
+ yaffs_flush_whole_cache(dev);
+ yaffs_checkpoint_save(dev);
+ retVal = 0;
+
+ }
+ } else
+ yaffsfs_SetError(-ENODEV);
+
+ yaffsfs_Unlock();
+ return retVal;
+}
+
+static int yaffsfs_IsDevBusy(struct yaffs_dev *dev)
+{
+ int i;
+ struct yaffs_obj *obj;
+
+ for (i = 0; i < YAFFSFS_N_HANDLES; i++) {
+ obj = yaffsfs_HandleToObject(i);
+ if (obj && obj->my_dev == dev)
+ return 1;
+ }
+ return 0;
+}
+
+int yaffs_remount(const YCHAR *path, int force, int read_only)
+{
+ int retVal = -1;
+ struct yaffs_dev *dev = NULL;
+
+ if (!path) {
+ yaffsfs_SetError(-EFAULT);
+ return -1;
+ }
+
+ if (yaffsfs_CheckPath(path) < 0) {
+ yaffsfs_SetError(-ENAMETOOLONG);
+ return -1;
+ }
+
+ yaffsfs_Lock();
+ dev = yaffsfs_FindMountPoint(path);
+ if (dev) {
+ if (dev->is_mounted) {
+ yaffs_flush_whole_cache(dev);
+
+ if (force || !yaffsfs_IsDevBusy(dev)) {
+ if (read_only)
+ yaffs_checkpoint_save(dev);
+ dev->read_only = read_only ? 1 : 0;
+ retVal = 0;
+ } else
+ yaffsfs_SetError(-EBUSY);
+
+ } else
+ yaffsfs_SetError(-EINVAL);
+
+ } else
+ yaffsfs_SetError(-ENODEV);
+
+ yaffsfs_Unlock();
+ return retVal;
+
+}
+
+int yaffs_unmount2(const YCHAR *path, int force)
+{
+ int retVal = -1;
+ struct yaffs_dev *dev = NULL;
+
+ if (!path) {
+ yaffsfs_SetError(-EFAULT);
+ return -1;
+ }
+
+ if (yaffsfs_CheckPath(path) < 0) {
+ yaffsfs_SetError(-ENAMETOOLONG);
+ return -1;
+ }
+
+ yaffsfs_Lock();
+ dev = yaffsfs_FindMountPoint(path);
+ if (dev) {
+ if (dev->is_mounted) {
+ int inUse;
+ yaffs_flush_whole_cache(dev);
+ yaffs_checkpoint_save(dev);
+ inUse = yaffsfs_IsDevBusy(dev);
+ if (!inUse || force) {
+ if (inUse)
+ yaffsfs_BreakDeviceHandles(dev);
+ yaffs_deinitialise(dev);
+
+ retVal = 0;
+ } else
+ yaffsfs_SetError(-EBUSY);
+
+ } else
+ yaffsfs_SetError(-EINVAL);
+
+ } else
+ yaffsfs_SetError(-ENODEV);
+
+ yaffsfs_Unlock();
+ return retVal;
+
+}
+
+int yaffs_unmount(const YCHAR *path)
+{
+ return yaffs_unmount2(path, 0);
+}
+
+loff_t yaffs_freespace(const YCHAR *path)
+{
+ loff_t retVal = -1;
+ struct yaffs_dev *dev = NULL;
+ YCHAR *dummy;
+
+ if (!path) {
+ yaffsfs_SetError(-EFAULT);
+ return -1;
+ }
+
+ if (yaffsfs_CheckPath(path) < 0) {
+ yaffsfs_SetError(-ENAMETOOLONG);
+ return -1;
+ }
+
+ yaffsfs_Lock();
+ dev = yaffsfs_FindDevice(path, &dummy);
+ if (dev && dev->is_mounted) {
+ retVal = yaffs_get_n_free_chunks(dev);
+ retVal *= dev->data_bytes_per_chunk;
+
+ } else
+ yaffsfs_SetError(-EINVAL);
+
+ yaffsfs_Unlock();
+ return retVal;
+}
+
+loff_t yaffs_totalspace(const YCHAR *path)
+{
+ loff_t retVal = -1;
+ struct yaffs_dev *dev = NULL;
+ YCHAR *dummy;
+
+ if (!path) {
+ yaffsfs_SetError(-EFAULT);
+ return -1;
+ }
+
+ if (yaffsfs_CheckPath(path) < 0) {
+ yaffsfs_SetError(-ENAMETOOLONG);
+ return -1;
+ }
+
+ yaffsfs_Lock();
+ dev = yaffsfs_FindDevice(path, &dummy);
+ if (dev && dev->is_mounted) {
+ retVal = (dev->param.end_block - dev->param.start_block + 1) -
+ dev->param.n_reserved_blocks;
+ retVal *= dev->param.chunks_per_block;
+ retVal *= dev->data_bytes_per_chunk;
+
+ } else
+ yaffsfs_SetError(-EINVAL);
+
+ yaffsfs_Unlock();
+ return retVal;
+}
+
+int yaffs_inodecount(const YCHAR *path)
+{
+ loff_t retVal = -1;
+ struct yaffs_dev *dev = NULL;
+ YCHAR *dummy;
+
+ if (!path) {
+ yaffsfs_SetError(-EFAULT);
+ return -1;
+ }
+
+ if (yaffsfs_CheckPath(path) < 0) {
+ yaffsfs_SetError(-ENAMETOOLONG);
+ return -1;
+ }
+
+ yaffsfs_Lock();
+ dev = yaffsfs_FindDevice(path, &dummy);
+ if (dev && dev->is_mounted) {
+ int n_obj = dev->n_obj;
+ if (n_obj > dev->n_hardlinks)
+ retVal = n_obj - dev->n_hardlinks;
+ }
+
+ if (retVal < 0)
+ yaffsfs_SetError(-EINVAL);
+
+ yaffsfs_Unlock();
+ return retVal;
+}
+
+void yaffs_add_device(struct yaffs_dev *dev)
+{
+ struct list_head *cfg;
+ /* First check that the device is not in the list. */
+
+ list_for_each(cfg, &yaffsfs_deviceList) {
+ if (dev == list_entry(cfg, struct yaffs_dev, dev_list))
+ return;
+ }
+
+ dev->is_mounted = 0;
+ dev->param.remove_obj_fn = yaffsfs_RemoveObjectCallback;
+
+ if (!dev->dev_list.next)
+ INIT_LIST_HEAD(&dev->dev_list);
+
+ list_add(&dev->dev_list, &yaffsfs_deviceList);
+}
+
+void yaffs_remove_device(struct yaffs_dev *dev)
+{
+ list_del_init(&dev->dev_list);
+}
+
+/* Functions to iterate through devices. NB Use with extreme care! */
+
+static struct list_head *dev_iterator;
+void yaffs_dev_rewind(void)
+{
+ dev_iterator = yaffsfs_deviceList.next;
+}
+
+struct yaffs_dev *yaffs_next_dev(void)
+{
+ struct yaffs_dev *retval;
+
+ if (!dev_iterator)
+ return NULL;
+ if (dev_iterator == &yaffsfs_deviceList)
+ return NULL;
+
+ retval = list_entry(dev_iterator, struct yaffs_dev, dev_list);
+ dev_iterator = dev_iterator->next;
+ return retval;
+}
+
+/* Directory search stuff. */
+
+static struct list_head search_contexts;
+
+static void yaffsfs_SetDirRewound(struct yaffsfs_DirSearchContxt *dsc)
+{
+ if (dsc &&
+ dsc->dirObj &&
+ dsc->dirObj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) {
+
+ dsc->offset = 0;
+
+ if (list_empty(&dsc->dirObj->variant.dir_variant.children))
+ dsc->nextReturn = NULL;
+ else
+ dsc->nextReturn =
+ list_entry(dsc->dirObj->variant.dir_variant.
+ children.next, struct yaffs_obj,
+ siblings);
+ } else {
+ /* Hey someone isn't playing nice! */
+ }
+}
+
+static void yaffsfs_DirAdvance(struct yaffsfs_DirSearchContxt *dsc)
+{
+ if (dsc &&
+ dsc->dirObj &&
+ dsc->dirObj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) {
+
+ if (dsc->nextReturn == NULL ||
+ list_empty(&dsc->dirObj->variant.dir_variant.children))
+ dsc->nextReturn = NULL;
+ else {
+ struct list_head *next = dsc->nextReturn->siblings.next;
+
+ if (next == &dsc->dirObj->variant.dir_variant.children)
+ dsc->nextReturn = NULL; /* end of list */
+ else
+ dsc->nextReturn = list_entry(next,
+ struct yaffs_obj,
+ siblings);
+ }
+ } else {
+ /* Hey someone isn't playing nice! */
+ }
+}
+
+static void yaffsfs_RemoveObjectCallback(struct yaffs_obj *obj)
+{
+
+ struct list_head *i;
+ struct yaffsfs_DirSearchContxt *dsc;
+
+ /* if search contexts not initilised then skip */
+ if (!search_contexts.next)
+ return;
+
+ /* Iterate through the directory search contexts.
+ * If any are the one being removed, then advance the dsc to
+ * the next one to prevent a hanging ptr.
+ */
+ list_for_each(i, &search_contexts) {
+ dsc = list_entry(i, struct yaffsfs_DirSearchContxt, others);
+ if (dsc->nextReturn == obj)
+ yaffsfs_DirAdvance(dsc);
+ }
+
+}
+
+yaffs_DIR *yaffs_opendir(const YCHAR *dirname)
+{
+ yaffs_DIR *dir = NULL;
+ struct yaffs_obj *obj = NULL;
+ struct yaffsfs_DirSearchContxt *dsc = NULL;
+ int notDir = 0;
+ int loop = 0;
+
+ if (!dirname) {
+ yaffsfs_SetError(-EFAULT);
+ return NULL;
+ }
+
+ if (yaffsfs_CheckPath(dirname) < 0) {
+ yaffsfs_SetError(-ENAMETOOLONG);
+ return NULL;
+ }
+
+ yaffsfs_Lock();
+
+ obj = yaffsfs_FindObject(NULL, dirname, 0, 1, NULL, &notDir, &loop);
+
+ if (!obj && notDir)
+ yaffsfs_SetError(-ENOTDIR);
+ else if (loop)
+ yaffsfs_SetError(-ELOOP);
+ else if (!obj)
+ yaffsfs_SetError(-ENOENT);
+ else if (obj->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY)
+ yaffsfs_SetError(-ENOTDIR);
+ else {
+ int i;
+
+ for (i = 0, dsc = NULL; i < YAFFSFS_N_DSC && !dsc; i++) {
+ if (!yaffsfs_dsc[i].inUse)
+ dsc = &yaffsfs_dsc[i];
+ }
+
+ dir = (yaffs_DIR *) dsc;
+
+ if (dsc) {
+ memset(dsc, 0, sizeof(struct yaffsfs_DirSearchContxt));
+ dsc->inUse = 1;
+ dsc->dirObj = obj;
+ yaffs_strncpy(dsc->name, dirname, NAME_MAX);
+ INIT_LIST_HEAD(&dsc->others);
+
+ if (!search_contexts.next)
+ INIT_LIST_HEAD(&search_contexts);
+
+ list_add(&dsc->others, &search_contexts);
+ yaffsfs_SetDirRewound(dsc);
+ }
+
+ }
+
+ yaffsfs_Unlock();
+
+ return dir;
+}
+
+struct yaffs_dirent *yaffs_readdir(yaffs_DIR * dirp)
+{
+ struct yaffsfs_DirSearchContxt *dsc;
+ struct yaffs_dirent *retVal = NULL;
+
+ dsc = (struct yaffsfs_DirSearchContxt *) dirp;
+ yaffsfs_Lock();
+
+ if (dsc && dsc->inUse) {
+ yaffsfs_SetError(0);
+ if (dsc->nextReturn) {
+ dsc->de.d_ino =
+ yaffs_get_equivalent_obj(dsc->nextReturn)->obj_id;
+ dsc->de.d_dont_use = (unsigned)dsc->nextReturn;
+ dsc->de.d_off = dsc->offset++;
+ yaffs_get_obj_name(dsc->nextReturn,
+ dsc->de.d_name, NAME_MAX);
+ if (yaffs_strnlen(dsc->de.d_name, NAME_MAX + 1) == 0) {
+ /* this should not happen! */
+ yaffs_strcpy(dsc->de.d_name, _Y("zz"));
+ }
+ dsc->de.d_reclen = sizeof(struct yaffs_dirent);
+ retVal = &dsc->de;
+ yaffsfs_DirAdvance(dsc);
+ } else
+ retVal = NULL;
+ } else
+ yaffsfs_SetError(-EBADF);
+
+ yaffsfs_Unlock();
+
+ return retVal;
+
+}
+
+void yaffs_rewinddir(yaffs_DIR *dirp)
+{
+ struct yaffsfs_DirSearchContxt *dsc;
+
+ dsc = (struct yaffsfs_DirSearchContxt *) dirp;
+
+ yaffsfs_Lock();
+
+ yaffsfs_SetDirRewound(dsc);
+
+ yaffsfs_Unlock();
+}
+
+int yaffs_closedir(yaffs_DIR *dirp)
+{
+ struct yaffsfs_DirSearchContxt *dsc;
+
+ dsc = (struct yaffsfs_DirSearchContxt *) dirp;
+
+ if (!dsc) {
+ yaffsfs_SetError(-EFAULT);
+ return -1;
+ }
+
+ yaffsfs_Lock();
+ dsc->inUse = 0;
+ list_del(&dsc->others); /* unhook from list */
+ yaffsfs_Unlock();
+ return 0;
+}
+
+/* End of directory stuff */
+
+int yaffs_symlink(const YCHAR *oldpath, const YCHAR *newpath)
+{
+ struct yaffs_obj *parent = NULL;
+ struct yaffs_obj *obj;
+ YCHAR *name;
+ int retVal = -1;
+ int mode = 0; /* ignore for now */
+ int notDir = 0;
+ int loop = 0;
+
+ if (!oldpath || !newpath) {
+ yaffsfs_SetError(-EFAULT);
+ return -1;
+ }
+
+ if (yaffsfs_CheckPath(newpath) < 0 || yaffsfs_CheckPath(oldpath) < 0) {
+ yaffsfs_SetError(-ENAMETOOLONG);
+ return -1;
+ }
+
+ yaffsfs_Lock();
+ parent = yaffsfs_FindDirectory(NULL, newpath, &name, 0, &notDir, &loop);
+ if (!parent && notDir)
+ yaffsfs_SetError(-ENOTDIR);
+ else if (loop)
+ yaffsfs_SetError(-ELOOP);
+ else if (!parent || yaffs_strnlen(name, 5) < 1)
+ yaffsfs_SetError(-ENOENT);
+ else if (yaffsfs_TooManyObjects(parent->my_dev))
+ yaffsfs_SetError(-ENFILE);
+ else if (parent->my_dev->read_only)
+ yaffsfs_SetError(-EROFS);
+ else {
+ obj = yaffs_create_symlink(parent, name, mode, 0, 0, oldpath);
+ if (obj)
+ retVal = 0;
+ else if (yaffsfs_FindObject
+ (NULL, newpath, 0, 0, NULL, NULL, NULL))
+ yaffsfs_SetError(-EEXIST);
+ else
+ yaffsfs_SetError(-ENOSPC);
+ }
+
+ yaffsfs_Unlock();
+
+ return retVal;
+
+}
+
+int yaffs_readlink(const YCHAR *path, YCHAR *buf, int bufsiz)
+{
+ struct yaffs_obj *obj = NULL;
+ struct yaffs_obj *dir = NULL;
+ int retVal = -1;
+ int notDir = 0;
+ int loop = 0;
+
+ if (!path || !buf) {
+ yaffsfs_SetError(-EFAULT);
+ return -1;
+ }
+
+ yaffsfs_Lock();
+
+ obj = yaffsfs_FindObject(NULL, path, 0, 1, &dir, &notDir, &loop);
+
+ if (!dir && notDir)
+ yaffsfs_SetError(-ENOTDIR);
+ else if (loop)
+ yaffsfs_SetError(-ELOOP);
+ else if (!dir || !obj)
+ yaffsfs_SetError(-ENOENT);
+ else if (obj->variant_type != YAFFS_OBJECT_TYPE_SYMLINK)
+ yaffsfs_SetError(-EINVAL);
+ else {
+ YCHAR *alias = obj->variant.symlink_variant.alias;
+ memset(buf, 0, bufsiz);
+ yaffs_strncpy(buf, alias, bufsiz - 1);
+ retVal = 0;
+ }
+ yaffsfs_Unlock();
+ return retVal;
+}
+
+int yaffs_link(const YCHAR *oldpath, const YCHAR *linkpath)
+{
+ /* Creates a link called newpath to existing oldpath */
+ struct yaffs_obj *obj = NULL;
+ struct yaffs_obj *lnk = NULL;
+ struct yaffs_obj *obj_dir = NULL;
+ struct yaffs_obj *lnk_dir = NULL;
+ int retVal = -1;
+ int notDirObj = 0;
+ int notDirLnk = 0;
+ int objLoop = 0;
+ int lnkLoop = 0;
+ YCHAR *newname;
+
+ if (!oldpath || !linkpath) {
+ yaffsfs_SetError(-EFAULT);
+ return -1;
+ }
+
+ if (yaffsfs_CheckPath(linkpath) < 0 || yaffsfs_CheckPath(oldpath) < 0) {
+ yaffsfs_SetError(-ENAMETOOLONG);
+ return -1;
+ }
+
+ yaffsfs_Lock();
+
+ obj = yaffsfs_FindObject(NULL, oldpath, 0, 1,
+ &obj_dir, &notDirObj, &objLoop);
+ lnk = yaffsfs_FindObject(NULL, linkpath, 0, 0, NULL, NULL, NULL);
+ lnk_dir = yaffsfs_FindDirectory(NULL, linkpath, &newname,
+ 0, &notDirLnk, &lnkLoop);
+
+ if ((!obj_dir && notDirObj) || (!lnk_dir && notDirLnk))
+ yaffsfs_SetError(-ENOTDIR);
+ else if (objLoop || lnkLoop)
+ yaffsfs_SetError(-ELOOP);
+ else if (!obj_dir || !lnk_dir || !obj)
+ yaffsfs_SetError(-ENOENT);
+ else if (obj->my_dev->read_only)
+ yaffsfs_SetError(-EROFS);
+ else if (yaffsfs_TooManyObjects(obj->my_dev))
+ yaffsfs_SetError(-ENFILE);
+ else if (lnk)
+ yaffsfs_SetError(-EEXIST);
+ else if (lnk_dir->my_dev != obj->my_dev)
+ yaffsfs_SetError(-EXDEV);
+ else {
+ retVal = yaffsfs_CheckNameLength(newname);
+
+ if (retVal == 0) {
+ lnk = yaffs_link_obj(lnk_dir, newname, obj);
+ if (lnk)
+ retVal = 0;
+ else {
+ yaffsfs_SetError(-ENOSPC);
+ retVal = -1;
+ }
+ }
+ }
+ yaffsfs_Unlock();
+
+ return retVal;
+}
+
+int yaffs_mknod(const YCHAR *pathname, mode_t mode, dev_t dev)
+{
+ yaffsfs_SetError(-EINVAL);
+ return -1;
+}
+
+/*
+ * D E B U G F U N C T I O N S
+ */
+
+/*
+ * yaffs_n_handles()
+ * Returns number of handles attached to the object
+ */
+int yaffs_n_handles(const YCHAR *path)
+{
+ struct yaffs_obj *obj;
+
+ if (!path) {
+ yaffsfs_SetError(-EFAULT);
+ return -1;
+ }
+
+ if (yaffsfs_CheckPath(path) < 0) {
+ yaffsfs_SetError(-ENAMETOOLONG);
+ return -1;
+ }
+
+ obj = yaffsfs_FindObject(NULL, path, 0, 1, NULL, NULL, NULL);
+
+ if (obj)
+ return yaffsfs_CountHandles(obj);
+ else
+ return -1;
+}
+
+int yaffs_get_error(void)
+{
+ return yaffsfs_GetLastError();
+}
+
+int yaffs_set_error(int error)
+{
+ yaffsfs_SetError(error);
+ return 0;
+}
+
+int yaffs_dump_dev(const YCHAR *path)
+{
+#if 0
+ YCHAR *rest;
+
+ struct yaffs_obj *obj = yaffsfs_FindRoot(path, &rest);
+
+ if (obj) {
+ struct yaffs_dev *dev = obj->my_dev;
+
+ printf("\n"
+ "n_page_writes.......... %d\n"
+ "n_page_reads........... %d\n"
+ "n_erasures....... %d\n"
+ "n_gc_copies............ %d\n"
+ "garbageCollections... %d\n"
+ "passiveGarbageColl'ns %d\n"
+ "\n",
+ dev->n_page_writes,
+ dev->n_page_reads,
+ dev->n_erasures,
+ dev->n_gc_copies,
+ dev->garbageCollections, dev->passiveGarbageCollections);
+
+ }
+#endif
+ return 0;
+}
diff --git a/roms/u-boot/fs/yaffs2/yaffsfs.h b/roms/u-boot/fs/yaffs2/yaffsfs.h
new file mode 100644
index 000000000..f2c766662
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yaffsfs.h
@@ -0,0 +1,209 @@
+/*
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ */
+
+/*
+ * Header file for using yaffs in an application via
+ * a direct interface.
+ */
+
+
+#ifndef __YAFFSFS_H__
+#define __YAFFSFS_H__
+
+#include "yaffscfg.h"
+#include "yportenv.h"
+
+
+#ifndef NAME_MAX
+#define NAME_MAX 256
+#endif
+
+#define YAFFS_MAX_FILE_SIZE (0x800000000LL - 1)
+
+
+struct yaffs_dirent {
+ long d_ino; /* inode number */
+ off_t d_off; /* offset to this dirent */
+ unsigned short d_reclen; /* length of this dirent */
+ YUCHAR d_type; /* type of this record */
+ YCHAR d_name[NAME_MAX+1]; /* file name (null-terminated) */
+ unsigned d_dont_use; /* debug: not for public consumption */
+};
+
+typedef struct opaque_structure yaffs_DIR;
+
+
+
+struct yaffs_stat {
+ int st_dev; /* device */
+ int st_ino; /* inode */
+ unsigned st_mode; /* protection */
+ int st_nlink; /* number of hard links */
+ int st_uid; /* user ID of owner */
+ int st_gid; /* group ID of owner */
+ unsigned st_rdev; /* device type (if inode device) */
+ loff_t st_size; /* total size, in bytes */
+ unsigned long st_blksize; /* blocksize for filesystem I/O */
+ unsigned long st_blocks; /* number of blocks allocated */
+#ifdef CONFIG_YAFFS_WINCE
+ /* Special 64-bit times for WinCE */
+ unsigned long yst_wince_atime[2];
+ unsigned long yst_wince_mtime[2];
+ unsigned long yst_wince_ctime[2];
+#else
+ unsigned long yst_atime; /* time of last access */
+ unsigned long yst_mtime; /* time of last modification */
+ unsigned long yst_ctime; /* time of last change */
+#endif
+};
+
+
+struct yaffs_utimbuf {
+ unsigned long actime;
+ unsigned long modtime;
+};
+
+
+int yaffs_open(const YCHAR *path, int oflag, int mode) ;
+
+int yaffs_close(int fd) ;
+int yaffs_fsync(int fd) ;
+int yaffs_fdatasync(int fd) ;
+int yaffs_flush(int fd) ; /* same as yaffs_fsync() */
+
+int yaffs_access(const YCHAR *path, int amode);
+
+int yaffs_dup(int fd);
+
+int yaffs_read(int fd, void *buf, unsigned int nbyte) ;
+int yaffs_write(int fd, const void *buf, unsigned int nbyte) ;
+
+int yaffs_pread(int fd, void *buf, unsigned int nbyte, loff_t offset);
+int yaffs_pwrite(int fd, const void *buf, unsigned int nbyte, loff_t offset);
+
+loff_t yaffs_lseek(int fd, loff_t offset, int whence) ;
+
+int yaffs_truncate(const YCHAR *path, loff_t new_size);
+int yaffs_ftruncate(int fd, loff_t new_size);
+
+int yaffs_unlink(const YCHAR *path) ;
+int yaffs_rename(const YCHAR *oldPath, const YCHAR *newPath) ;
+
+int yaffs_stat(const YCHAR *path, struct yaffs_stat *buf) ;
+int yaffs_lstat(const YCHAR *path, struct yaffs_stat *buf) ;
+int yaffs_fstat(int fd, struct yaffs_stat *buf) ;
+
+int yaffs_utime(const YCHAR *path, const struct yaffs_utimbuf *buf);
+int yaffs_futime(int fd, const struct yaffs_utimbuf *buf);
+
+
+int yaffs_setxattr(const char *path, const char *name,
+ const void *data, int size, int flags);
+int yaffs_lsetxattr(const char *path, const char *name,
+ const void *data, int size, int flags);
+int yaffs_fsetxattr(int fd, const char *name,
+ const void *data, int size, int flags);
+
+int yaffs_getxattr(const char *path, const char *name,
+ void *data, int size);
+int yaffs_lgetxattr(const char *path, const char *name,
+ void *data, int size);
+int yaffs_fgetxattr(int fd, const char *name,
+ void *data, int size);
+
+int yaffs_removexattr(const char *path, const char *name);
+int yaffs_lremovexattr(const char *path, const char *name);
+int yaffs_fremovexattr(int fd, const char *name);
+
+int yaffs_listxattr(const char *path, char *list, int size);
+int yaffs_llistxattr(const char *path, char *list, int size);
+int yaffs_flistxattr(int fd, char *list, int size);
+
+
+#ifdef CONFIG_YAFFS_WINCE
+
+int yaffs_set_wince_times(int fd,
+ const unsigned *wctime,
+ const unsigned *watime,
+ const unsigned *wmtime);
+int yaffs_get_wince_times(int fd,
+ unsigned *wctime,
+ unsigned *watime,
+ unsigned *wmtime);
+
+#endif
+
+int yaffs_chmod(const YCHAR *path, mode_t mode);
+int yaffs_fchmod(int fd, mode_t mode);
+
+int yaffs_mkdir(const YCHAR *path, mode_t mode) ;
+int yaffs_rmdir(const YCHAR *path) ;
+
+yaffs_DIR *yaffs_opendir(const YCHAR *dirname) ;
+struct yaffs_dirent *yaffs_readdir(yaffs_DIR *dirp) ;
+void yaffs_rewinddir(yaffs_DIR *dirp) ;
+int yaffs_closedir(yaffs_DIR *dirp) ;
+
+int yaffs_mount(const YCHAR *path) ;
+int yaffs_mount2(const YCHAR *path, int read_only);
+int yaffs_mount_common(const YCHAR *path, int read_only, int skip_checkpt);
+
+int yaffs_unmount(const YCHAR *path) ;
+int yaffs_unmount2(const YCHAR *path, int force);
+int yaffs_remount(const YCHAR *path, int force, int read_only);
+
+
+int yaffs_sync(const YCHAR *path) ;
+
+int yaffs_symlink(const YCHAR *oldpath, const YCHAR *newpath);
+int yaffs_readlink(const YCHAR *path, YCHAR *buf, int bufsiz);
+
+int yaffs_link(const YCHAR *oldpath, const YCHAR *newpath);
+int yaffs_mknod(const YCHAR *pathname, mode_t mode, dev_t dev);
+
+loff_t yaffs_freespace(const YCHAR *path);
+loff_t yaffs_totalspace(const YCHAR *path);
+
+int yaffs_inodecount(const YCHAR *path);
+
+int yaffs_n_handles(const YCHAR *path);
+
+#define YAFFS_SHARE_READ 1
+#define YAFFS_SHARE_WRITE 2
+int yaffs_open_sharing(const YCHAR *path, int oflag, int mode, int shareMode);
+
+struct yaffs_dev;
+void yaffs_add_device(struct yaffs_dev *dev);
+
+int yaffs_start_up(void);
+int yaffsfs_GetLastError(void);
+
+/* Functions to iterate through devices. NB Use with extreme care! */
+void yaffs_dev_rewind(void);
+struct yaffs_dev *yaffs_next_dev(void);
+
+/* Function to get the last error */
+int yaffs_get_error(void);
+const char *yaffs_error_to_str(int err);
+
+/* Function only for debugging */
+void *yaffs_getdev(const YCHAR *path);
+int yaffs_dump_dev(const YCHAR *path);
+int yaffs_set_error(int error);
+
+/* Trace control functions */
+unsigned yaffs_set_trace(unsigned tm);
+unsigned yaffs_get_trace(void);
+#endif
diff --git a/roms/u-boot/fs/yaffs2/ydirectenv.h b/roms/u-boot/fs/yaffs2/ydirectenv.h
new file mode 100644
index 000000000..d274f22e8
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/ydirectenv.h
@@ -0,0 +1,82 @@
+/*
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ */
+
+/*
+ * ydirectenv.h: Environment wrappers for YAFFS direct.
+ */
+
+#ifndef __YDIRECTENV_H__
+#define __YDIRECTENV_H__
+
+#include <common.h>
+#include <malloc.h>
+#include <linux/compat.h>
+
+#include "yaffs_osglue.h"
+
+void yaffs_bug_fn(const char *file_name, int line_no);
+
+
+
+#define YCHAR char
+#define YUCHAR unsigned char
+#define _Y(x) x
+
+#define yaffs_strcat(a, b) strcat(a, b)
+#define yaffs_strcpy(a, b) strcpy(a, b)
+#define yaffs_strncpy(a, b, c) strncpy(a, b, c)
+#define yaffs_strnlen(s, m) strnlen(s, m)
+#ifdef CONFIG_YAFFS_CASE_INSENSITIVE
+#define yaffs_strcmp(a, b) strcasecmp(a, b)
+#define yaffs_strncmp(a, b, c) strncasecmp(a, b, c)
+#else
+#define yaffs_strcmp(a, b) strcmp(a, b)
+#define yaffs_strncmp(a, b, c) strncmp(a, b, c)
+#endif
+
+
+void yaffs_qsort(void *aa, size_t n, size_t es,
+ int (*cmp)(const void *, const void *));
+
+#define sort(base, n, sz, cmp_fn, swp) yaffs_qsort(base, n, sz, cmp_fn)
+
+#define YAFFS_PATH_DIVIDERS "/"
+
+#ifdef NO_inline
+#define inline
+#endif
+
+#define yaffs_trace(msk, fmt, ...) do { \
+ if (yaffs_trace_mask & (msk)) \
+ printf("yaffs: " fmt "\n", ##__VA_ARGS__); \
+} while (0)
+
+
+#define YAFFS_LOSTNFOUND_NAME "lost+found"
+#define YAFFS_LOSTNFOUND_PREFIX "obj"
+
+#include "yaffscfg.h"
+
+#define Y_CURRENT_TIME yaffsfs_CurrentTime()
+#define Y_TIME_CONVERT(x) x
+
+#define YAFFS_ROOT_MODE 0666
+#define YAFFS_LOSTNFOUND_MODE 0666
+
+#include <linux/list.h>
+
+#include "yaffsfs.h"
+
+#endif
diff --git a/roms/u-boot/fs/yaffs2/yportenv.h b/roms/u-boot/fs/yaffs2/yportenv.h
new file mode 100644
index 000000000..081391a1d
--- /dev/null
+++ b/roms/u-boot/fs/yaffs2/yportenv.h
@@ -0,0 +1,310 @@
+/*
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ */
+
+
+#ifndef __YPORTENV_H__
+#define __YPORTENV_H__
+
+#include <linux/bug.h>
+#include <linux/types.h>
+
+/* Definition of types */
+#ifdef CONFIG_YAFFS_DEFINES_TYPES
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned u32;
+#endif
+
+
+#ifdef CONFIG_YAFFS_PROVIDE_DEFS
+/* File types */
+
+
+#define DT_UNKNOWN 0
+#define DT_FIFO 1
+#define DT_CHR 2
+#define DT_DIR 4
+#define DT_BLK 6
+#define DT_REG 8
+#define DT_LNK 10
+#define DT_SOCK 12
+#define DT_WHT 14
+
+
+/*
+ * Attribute flags.
+ * These are or-ed together to select what has been changed.
+ */
+#define ATTR_MODE 1
+#define ATTR_UID 2
+#define ATTR_GID 4
+#define ATTR_SIZE 8
+#define ATTR_ATIME 16
+#define ATTR_MTIME 32
+#define ATTR_CTIME 64
+
+struct iattr {
+ unsigned int ia_valid;
+ unsigned ia_mode;
+ unsigned ia_uid;
+ unsigned ia_gid;
+ unsigned ia_size;
+ unsigned ia_atime;
+ unsigned ia_mtime;
+ unsigned ia_ctime;
+ unsigned int ia_attr_flags;
+};
+
+#endif
+
+
+
+#if defined CONFIG_YAFFS_WINCE
+
+#include "ywinceenv.h"
+
+
+#elif defined CONFIG_YAFFS_DIRECT
+
+/* Direct interface */
+#include "ydirectenv.h"
+
+#elif defined CONFIG_YAFFS_UTIL
+
+#include "yutilsenv.h"
+
+#else
+/* Should have specified a configuration type */
+#error Unknown configuration
+
+#endif
+
+#if defined(CONFIG_YAFFS_DIRECT) || defined(CONFIG_YAFFS_WINCE)
+
+#ifdef CONFIG_YAFFSFS_PROVIDE_VALUES
+
+#ifndef O_RDONLY
+#define O_RDONLY 00
+#endif
+
+#ifndef O_WRONLY
+#define O_WRONLY 01
+#endif
+
+#ifndef O_RDWR
+#define O_RDWR 02
+#endif
+
+#ifndef O_CREAT
+#define O_CREAT 0100
+#endif
+
+#ifndef O_EXCL
+#define O_EXCL 0200
+#endif
+
+#ifndef O_TRUNC
+#define O_TRUNC 01000
+#endif
+
+#ifndef O_APPEND
+#define O_APPEND 02000
+#endif
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+
+#ifndef SEEK_CUR
+#define SEEK_CUR 1
+#endif
+
+#ifndef SEEK_END
+#define SEEK_END 2
+#endif
+
+#ifndef EBUSY
+#define EBUSY 16
+#endif
+
+#ifndef ENODEV
+#define ENODEV 19
+#endif
+
+#ifndef EINVAL
+#define EINVAL 22
+#endif
+
+#ifndef ENFILE
+#define ENFILE 23
+#endif
+
+#ifndef EBADF
+#define EBADF 9
+#endif
+
+#ifndef EACCES
+#define EACCES 13
+#endif
+
+#ifndef EXDEV
+#define EXDEV 18
+#endif
+
+#ifndef ENOENT
+#define ENOENT 2
+#endif
+
+#ifndef ENOSPC
+#define ENOSPC 28
+#endif
+
+#ifndef EROFS
+#define EROFS 30
+#endif
+
+#ifndef ERANGE
+#define ERANGE 34
+#endif
+
+#ifndef ENODATA
+#define ENODATA 61
+#endif
+
+#ifndef ENOTEMPTY
+#define ENOTEMPTY 39
+#endif
+
+#ifndef ENAMETOOLONG
+#define ENAMETOOLONG 36
+#endif
+
+#ifndef ENOMEM
+#define ENOMEM 12
+#endif
+
+#ifndef EFAULT
+#define EFAULT 14
+#endif
+
+#ifndef EEXIST
+#define EEXIST 17
+#endif
+
+#ifndef ENOTDIR
+#define ENOTDIR 20
+#endif
+
+#ifndef EISDIR
+#define EISDIR 21
+#endif
+
+#ifndef ELOOP
+#define ELOOP 40
+#endif
+
+
+/* Mode flags */
+
+#ifndef S_IFMT
+#define S_IFMT 0170000
+#endif
+
+#ifndef S_IFSOCK
+#define S_IFSOCK 0140000
+#endif
+
+#ifndef S_IFIFO
+#define S_IFIFO 0010000
+#endif
+
+#ifndef S_IFCHR
+#define S_IFCHR 0020000
+#endif
+
+#ifndef S_IFBLK
+#define S_IFBLK 0060000
+#endif
+
+#ifndef S_IFLNK
+#define S_IFLNK 0120000
+#endif
+
+#ifndef S_IFDIR
+#define S_IFDIR 0040000
+#endif
+
+#ifndef S_IFREG
+#define S_IFREG 0100000
+#endif
+
+#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
+#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
+#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
+#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
+#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
+
+
+#ifndef S_IREAD
+#define S_IREAD 0000400
+#endif
+
+#ifndef S_IWRITE
+#define S_IWRITE 0000200
+#endif
+
+#ifndef S_IEXEC
+#define S_IEXEC 0000100
+#endif
+
+#ifndef XATTR_CREATE
+#define XATTR_CREATE 1
+#endif
+
+#ifndef XATTR_REPLACE
+#define XATTR_REPLACE 2
+#endif
+
+#ifndef R_OK
+#define R_OK 4
+#define W_OK 2
+#define X_OK 1
+#define F_OK 0
+#endif
+
+#else
+#include <errno.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#endif
+
+#endif
+
+#ifndef Y_DUMP_STACK
+#define Y_DUMP_STACK() do { } while (0)
+#endif
+
+#ifndef BUG
+#define BUG() do {\
+ yaffs_trace(YAFFS_TRACE_BUG,\
+ "==>> yaffs bug: " __FILE__ " %d",\
+ __LINE__);\
+ Y_DUMP_STACK();\
+} while (0)
+#endif
+
+#endif
diff --git a/roms/u-boot/fs/zfs/Makefile b/roms/u-boot/fs/zfs/Makefile
new file mode 100644
index 000000000..37a0547e8
--- /dev/null
+++ b/roms/u-boot/fs/zfs/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright 2012
+# Jorgen Lundman <lundman at lundman.net>
+
+obj-y := dev.o zfs.o zfs_fletcher.o zfs_sha256.o zfs_lzjb.o
diff --git a/roms/u-boot/fs/zfs/dev.c b/roms/u-boot/fs/zfs/dev.c
new file mode 100644
index 000000000..251e7d1f7
--- /dev/null
+++ b/roms/u-boot/fs/zfs/dev.c
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ *
+ * based on code of fs/reiserfs/dev.c by
+ *
+ * (C) Copyright 2003 - 2004
+ * Sysgo AG, <www.elinos.com>, Pavel Bartusek <pba@sysgo.com>
+ */
+
+
+#include <common.h>
+#include <config.h>
+#include <fs_internal.h>
+#include <zfs_common.h>
+
+static struct blk_desc *zfs_blk_desc;
+static struct disk_partition *part_info;
+
+void zfs_set_blk_dev(struct blk_desc *rbdd, struct disk_partition *info)
+{
+ zfs_blk_desc = rbdd;
+ part_info = info;
+}
+
+/* err */
+int zfs_devread(int sector, int byte_offset, int byte_len, char *buf)
+{
+ return fs_devread(zfs_blk_desc, part_info, sector, byte_offset,
+ byte_len, buf);
+}
diff --git a/roms/u-boot/fs/zfs/zfs.c b/roms/u-boot/fs/zfs/zfs.c
new file mode 100644
index 000000000..1fec96cd5
--- /dev/null
+++ b/roms/u-boot/fs/zfs/zfs.c
@@ -0,0 +1,2341 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ *
+ * ZFS filesystem ported to u-boot by
+ * Jorgen Lundman <lundman at lundman.net>
+ *
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 1999,2000,2001,2002,2003,2004
+ * Free Software Foundation, Inc.
+ * Copyright 2004 Sun Microsystems, Inc.
+ */
+
+#include <common.h>
+#include <log.h>
+#include <malloc.h>
+#include <linux/stat.h>
+#include <linux/time.h>
+#include <linux/ctype.h>
+#include <asm/byteorder.h>
+#include "zfs_common.h"
+#include "div64.h"
+
+struct blk_desc *zfs_dev_desc;
+
+/*
+ * The zfs plug-in routines for GRUB are:
+ *
+ * zfs_mount() - locates a valid uberblock of the root pool and reads
+ * in its MOS at the memory address MOS.
+ *
+ * zfs_open() - locates a plain file object by following the MOS
+ * and places its dnode at the memory address DNODE.
+ *
+ * zfs_read() - read in the data blocks pointed by the DNODE.
+ *
+ */
+
+#include <zfs/zfs.h>
+#include <zfs/zio.h>
+#include <zfs/dnode.h>
+#include <zfs/uberblock_impl.h>
+#include <zfs/vdev_impl.h>
+#include <zfs/zio_checksum.h>
+#include <zfs/zap_impl.h>
+#include <zfs/zap_leaf.h>
+#include <zfs/zfs_znode.h>
+#include <zfs/dmu.h>
+#include <zfs/dmu_objset.h>
+#include <zfs/sa_impl.h>
+#include <zfs/dsl_dir.h>
+#include <zfs/dsl_dataset.h>
+
+
+#define ZPOOL_PROP_BOOTFS "bootfs"
+
+
+/*
+ * For nvlist manipulation. (from nvpair.h)
+ */
+#define NV_ENCODE_NATIVE 0
+#define NV_ENCODE_XDR 1
+#define NV_BIG_ENDIAN 0
+#define NV_LITTLE_ENDIAN 1
+#define DATA_TYPE_UINT64 8
+#define DATA_TYPE_STRING 9
+#define DATA_TYPE_NVLIST 19
+#define DATA_TYPE_NVLIST_ARRAY 20
+
+
+/*
+ * Macros to get fields in a bp or DVA.
+ */
+#define P2PHASE(x, align) ((x) & ((align) - 1))
+#define DVA_OFFSET_TO_PHYS_SECTOR(offset) \
+ ((offset + VDEV_LABEL_START_SIZE) >> SPA_MINBLOCKSHIFT)
+
+/*
+ * return x rounded down to an align boundary
+ * eg, P2ALIGN(1200, 1024) == 1024 (1*align)
+ * eg, P2ALIGN(1024, 1024) == 1024 (1*align)
+ * eg, P2ALIGN(0x1234, 0x100) == 0x1200 (0x12*align)
+ * eg, P2ALIGN(0x5600, 0x100) == 0x5600 (0x56*align)
+ */
+#define P2ALIGN(x, align) ((x) & -(align))
+
+/*
+ * FAT ZAP data structures
+ */
+#define ZFS_CRC64_POLY 0xC96C5795D7870F42ULL /* ECMA-182, reflected form */
+#define ZAP_HASH_IDX(hash, n) (((n) == 0) ? 0 : ((hash) >> (64 - (n))))
+#define CHAIN_END 0xffff /* end of the chunk chain */
+
+/*
+ * The amount of space within the chunk available for the array is:
+ * chunk size - space for type (1) - space for next pointer (2)
+ */
+#define ZAP_LEAF_ARRAY_BYTES (ZAP_LEAF_CHUNKSIZE - 3)
+
+#define ZAP_LEAF_HASH_SHIFT(bs) (bs - 5)
+#define ZAP_LEAF_HASH_NUMENTRIES(bs) (1 << ZAP_LEAF_HASH_SHIFT(bs))
+#define LEAF_HASH(bs, h) \
+ ((ZAP_LEAF_HASH_NUMENTRIES(bs)-1) & \
+ ((h) >> (64 - ZAP_LEAF_HASH_SHIFT(bs)-l->l_hdr.lh_prefix_len)))
+
+/*
+ * The amount of space available for chunks is:
+ * block size shift - hash entry size (2) * number of hash
+ * entries - header space (2*chunksize)
+ */
+#define ZAP_LEAF_NUMCHUNKS(bs) \
+ (((1<<bs) - 2*ZAP_LEAF_HASH_NUMENTRIES(bs)) / \
+ ZAP_LEAF_CHUNKSIZE - 2)
+
+/*
+ * The chunks start immediately after the hash table. The end of the
+ * hash table is at l_hash + HASH_NUMENTRIES, which we simply cast to a
+ * chunk_t.
+ */
+#define ZAP_LEAF_CHUNK(l, bs, idx) \
+ ((zap_leaf_chunk_t *)(l->l_hash + ZAP_LEAF_HASH_NUMENTRIES(bs)))[idx]
+#define ZAP_LEAF_ENTRY(l, bs, idx) (&ZAP_LEAF_CHUNK(l, bs, idx).l_entry)
+
+
+/*
+ * Decompression Entry - lzjb
+ */
+#ifndef NBBY
+#define NBBY 8
+#endif
+
+
+
+typedef int zfs_decomp_func_t(void *s_start, void *d_start,
+ uint32_t s_len, uint32_t d_len);
+typedef struct decomp_entry {
+ char *name;
+ zfs_decomp_func_t *decomp_func;
+} decomp_entry_t;
+
+typedef struct dnode_end {
+ dnode_phys_t dn;
+ zfs_endian_t endian;
+} dnode_end_t;
+
+struct zfs_data {
+ /* cache for a file block of the currently zfs_open()-ed file */
+ char *file_buf;
+ uint64_t file_start;
+ uint64_t file_end;
+
+ /* XXX: ashift is per vdev, not per pool. We currently only ever touch
+ * a single vdev, but when/if raid-z or stripes are supported, this
+ * may need revision.
+ */
+ uint64_t vdev_ashift;
+ uint64_t label_txg;
+ uint64_t pool_guid;
+
+ /* cache for a dnode block */
+ dnode_phys_t *dnode_buf;
+ dnode_phys_t *dnode_mdn;
+ uint64_t dnode_start;
+ uint64_t dnode_end;
+ zfs_endian_t dnode_endian;
+
+ uberblock_t current_uberblock;
+
+ dnode_end_t mos;
+ dnode_end_t mdn;
+ dnode_end_t dnode;
+
+ uint64_t vdev_phys_sector;
+
+ int (*userhook)(const char *, const struct zfs_dirhook_info *);
+ struct zfs_dirhook_info *dirinfo;
+
+};
+
+
+
+
+static int
+zlib_decompress(void *s, void *d,
+ uint32_t slen, uint32_t dlen)
+{
+ if (zlib_decompress(s, d, slen, dlen) < 0)
+ return ZFS_ERR_BAD_FS;
+ return ZFS_ERR_NONE;
+}
+
+static decomp_entry_t decomp_table[ZIO_COMPRESS_FUNCTIONS] = {
+ {"inherit", NULL}, /* ZIO_COMPRESS_INHERIT */
+ {"on", lzjb_decompress}, /* ZIO_COMPRESS_ON */
+ {"off", NULL}, /* ZIO_COMPRESS_OFF */
+ {"lzjb", lzjb_decompress}, /* ZIO_COMPRESS_LZJB */
+ {"empty", NULL}, /* ZIO_COMPRESS_EMPTY */
+ {"gzip-1", zlib_decompress}, /* ZIO_COMPRESS_GZIP1 */
+ {"gzip-2", zlib_decompress}, /* ZIO_COMPRESS_GZIP2 */
+ {"gzip-3", zlib_decompress}, /* ZIO_COMPRESS_GZIP3 */
+ {"gzip-4", zlib_decompress}, /* ZIO_COMPRESS_GZIP4 */
+ {"gzip-5", zlib_decompress}, /* ZIO_COMPRESS_GZIP5 */
+ {"gzip-6", zlib_decompress}, /* ZIO_COMPRESS_GZIP6 */
+ {"gzip-7", zlib_decompress}, /* ZIO_COMPRESS_GZIP7 */
+ {"gzip-8", zlib_decompress}, /* ZIO_COMPRESS_GZIP8 */
+ {"gzip-9", zlib_decompress}, /* ZIO_COMPRESS_GZIP9 */
+};
+
+
+
+static int zio_read_data(blkptr_t *bp, zfs_endian_t endian,
+ void *buf, struct zfs_data *data);
+
+static int
+zio_read(blkptr_t *bp, zfs_endian_t endian, void **buf,
+ size_t *size, struct zfs_data *data);
+
+/*
+ * Our own version of log2(). Same thing as highbit()-1.
+ */
+static int
+zfs_log2(uint64_t num)
+{
+ int i = 0;
+
+ while (num > 1) {
+ i++;
+ num = num >> 1;
+ }
+
+ return i;
+}
+
+
+/* Checksum Functions */
+static void
+zio_checksum_off(const void *buf __attribute__ ((unused)),
+ uint64_t size __attribute__ ((unused)),
+ zfs_endian_t endian __attribute__ ((unused)),
+ zio_cksum_t *zcp)
+{
+ ZIO_SET_CHECKSUM(zcp, 0, 0, 0, 0);
+}
+
+/* Checksum Table and Values */
+static zio_checksum_info_t zio_checksum_table[ZIO_CHECKSUM_FUNCTIONS] = {
+ {NULL, 0, 0, "inherit"},
+ {NULL, 0, 0, "on"},
+ {zio_checksum_off, 0, 0, "off"},
+ {zio_checksum_SHA256, 1, 1, "label"},
+ {zio_checksum_SHA256, 1, 1, "gang_header"},
+ {NULL, 0, 0, "zilog"},
+ {fletcher_2_endian, 0, 0, "fletcher2"},
+ {fletcher_4_endian, 1, 0, "fletcher4"},
+ {zio_checksum_SHA256, 1, 0, "SHA256"},
+ {NULL, 0, 0, "zilog2"},
+};
+
+/*
+ * zio_checksum_verify: Provides support for checksum verification.
+ *
+ * Fletcher2, Fletcher4, and SHA256 are supported.
+ *
+ */
+static int
+zio_checksum_verify(zio_cksum_t zc, uint32_t checksum,
+ zfs_endian_t endian, char *buf, int size)
+{
+ zio_eck_t *zec = (zio_eck_t *) (buf + size) - 1;
+ zio_checksum_info_t *ci = &zio_checksum_table[checksum];
+ zio_cksum_t actual_cksum, expected_cksum;
+
+ if (checksum >= ZIO_CHECKSUM_FUNCTIONS || ci->ci_func == NULL) {
+ printf("zfs unknown checksum function %d\n", checksum);
+ return ZFS_ERR_NOT_IMPLEMENTED_YET;
+ }
+
+ if (ci->ci_eck) {
+ expected_cksum = zec->zec_cksum;
+ zec->zec_cksum = zc;
+ ci->ci_func(buf, size, endian, &actual_cksum);
+ zec->zec_cksum = expected_cksum;
+ zc = expected_cksum;
+ } else {
+ ci->ci_func(buf, size, endian, &actual_cksum);
+ }
+
+ if ((actual_cksum.zc_word[0] != zc.zc_word[0])
+ || (actual_cksum.zc_word[1] != zc.zc_word[1])
+ || (actual_cksum.zc_word[2] != zc.zc_word[2])
+ || (actual_cksum.zc_word[3] != zc.zc_word[3])) {
+ return ZFS_ERR_BAD_FS;
+ }
+
+ return ZFS_ERR_NONE;
+}
+
+/*
+ * vdev_uberblock_compare takes two uberblock structures and returns an integer
+ * indicating the more recent of the two.
+ * Return Value = 1 if ub2 is more recent
+ * Return Value = -1 if ub1 is more recent
+ * The most recent uberblock is determined using its transaction number and
+ * timestamp. The uberblock with the highest transaction number is
+ * considered "newer". If the transaction numbers of the two blocks match, the
+ * timestamps are compared to determine the "newer" of the two.
+ */
+static int
+vdev_uberblock_compare(uberblock_t *ub1, uberblock_t *ub2)
+{
+ zfs_endian_t ub1_endian, ub2_endian;
+ if (zfs_to_cpu64(ub1->ub_magic, LITTLE_ENDIAN) == UBERBLOCK_MAGIC)
+ ub1_endian = LITTLE_ENDIAN;
+ else
+ ub1_endian = BIG_ENDIAN;
+ if (zfs_to_cpu64(ub2->ub_magic, LITTLE_ENDIAN) == UBERBLOCK_MAGIC)
+ ub2_endian = LITTLE_ENDIAN;
+ else
+ ub2_endian = BIG_ENDIAN;
+
+ if (zfs_to_cpu64(ub1->ub_txg, ub1_endian)
+ < zfs_to_cpu64(ub2->ub_txg, ub2_endian))
+ return -1;
+ if (zfs_to_cpu64(ub1->ub_txg, ub1_endian)
+ > zfs_to_cpu64(ub2->ub_txg, ub2_endian))
+ return 1;
+
+ if (zfs_to_cpu64(ub1->ub_timestamp, ub1_endian)
+ < zfs_to_cpu64(ub2->ub_timestamp, ub2_endian))
+ return -1;
+ if (zfs_to_cpu64(ub1->ub_timestamp, ub1_endian)
+ > zfs_to_cpu64(ub2->ub_timestamp, ub2_endian))
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Three pieces of information are needed to verify an uberblock: the magic
+ * number, the version number, and the checksum.
+ *
+ * Currently Implemented: version number, magic number, label txg
+ * Need to Implement: checksum
+ *
+ */
+static int
+uberblock_verify(uberblock_t *uber, int offset, struct zfs_data *data)
+{
+ int err;
+ zfs_endian_t endian = UNKNOWN_ENDIAN;
+ zio_cksum_t zc;
+
+ if (uber->ub_txg < data->label_txg) {
+ debug("ignoring partially written label: uber_txg < label_txg %llu %llu\n",
+ uber->ub_txg, data->label_txg);
+ return ZFS_ERR_BAD_FS;
+ }
+
+ if (zfs_to_cpu64(uber->ub_magic, LITTLE_ENDIAN) == UBERBLOCK_MAGIC
+ && zfs_to_cpu64(uber->ub_version, LITTLE_ENDIAN) > 0
+ && zfs_to_cpu64(uber->ub_version, LITTLE_ENDIAN) <= SPA_VERSION)
+ endian = LITTLE_ENDIAN;
+
+ if (zfs_to_cpu64(uber->ub_magic, BIG_ENDIAN) == UBERBLOCK_MAGIC
+ && zfs_to_cpu64(uber->ub_version, BIG_ENDIAN) > 0
+ && zfs_to_cpu64(uber->ub_version, BIG_ENDIAN) <= SPA_VERSION)
+ endian = BIG_ENDIAN;
+
+ if (endian == UNKNOWN_ENDIAN) {
+ printf("invalid uberblock magic\n");
+ return ZFS_ERR_BAD_FS;
+ }
+
+ memset(&zc, 0, sizeof(zc));
+ zc.zc_word[0] = cpu_to_zfs64(offset, endian);
+ err = zio_checksum_verify(zc, ZIO_CHECKSUM_LABEL, endian,
+ (char *) uber, UBERBLOCK_SIZE(data->vdev_ashift));
+
+ if (!err) {
+ /* Check that the data pointed by the rootbp is usable. */
+ void *osp = NULL;
+ size_t ospsize;
+ err = zio_read(&uber->ub_rootbp, endian, &osp, &ospsize, data);
+ free(osp);
+
+ if (!err && ospsize < OBJSET_PHYS_SIZE_V14) {
+ printf("uberblock rootbp points to invalid data\n");
+ return ZFS_ERR_BAD_FS;
+ }
+ }
+
+ return err;
+}
+
+/*
+ * Find the best uberblock.
+ * Return:
+ * Success - Pointer to the best uberblock.
+ * Failure - NULL
+ */
+static uberblock_t *find_bestub(char *ub_array, struct zfs_data *data)
+{
+ const uint64_t sector = data->vdev_phys_sector;
+ uberblock_t *ubbest = NULL;
+ uberblock_t *ubnext;
+ unsigned int i, offset, pickedub = 0;
+ int err = ZFS_ERR_NONE;
+
+ const unsigned int UBCOUNT = UBERBLOCK_COUNT(data->vdev_ashift);
+ const uint64_t UBBYTES = UBERBLOCK_SIZE(data->vdev_ashift);
+
+ for (i = 0; i < UBCOUNT; i++) {
+ ubnext = (uberblock_t *) (i * UBBYTES + ub_array);
+ offset = (sector << SPA_MINBLOCKSHIFT) + VDEV_PHYS_SIZE + (i * UBBYTES);
+
+ err = uberblock_verify(ubnext, offset, data);
+ if (err)
+ continue;
+
+ if (ubbest == NULL || vdev_uberblock_compare(ubnext, ubbest) > 0) {
+ ubbest = ubnext;
+ pickedub = i;
+ }
+ }
+
+ if (ubbest)
+ debug("zfs Found best uberblock at idx %d, txg %llu\n",
+ pickedub, (unsigned long long) ubbest->ub_txg);
+
+ return ubbest;
+}
+
+static inline size_t
+get_psize(blkptr_t *bp, zfs_endian_t endian)
+{
+ return (((zfs_to_cpu64((bp)->blk_prop, endian) >> 16) & 0xffff) + 1)
+ << SPA_MINBLOCKSHIFT;
+}
+
+static uint64_t
+dva_get_offset(dva_t *dva, zfs_endian_t endian)
+{
+ return zfs_to_cpu64((dva)->dva_word[1],
+ endian) << SPA_MINBLOCKSHIFT;
+}
+
+/*
+ * Read a block of data based on the gang block address dva,
+ * and put its data in buf.
+ *
+ */
+static int
+zio_read_gang(blkptr_t *bp, zfs_endian_t endian, dva_t *dva, void *buf,
+ struct zfs_data *data)
+{
+ zio_gbh_phys_t *zio_gb;
+ uint64_t offset, sector;
+ unsigned i;
+ int err;
+ zio_cksum_t zc;
+
+ memset(&zc, 0, sizeof(zc));
+
+ zio_gb = malloc(SPA_GANGBLOCKSIZE);
+ if (!zio_gb)
+ return ZFS_ERR_OUT_OF_MEMORY;
+
+ offset = dva_get_offset(dva, endian);
+ sector = DVA_OFFSET_TO_PHYS_SECTOR(offset);
+
+ /* read in the gang block header */
+ err = zfs_devread(sector, 0, SPA_GANGBLOCKSIZE, (char *) zio_gb);
+
+ if (err) {
+ free(zio_gb);
+ return err;
+ }
+
+ /* XXX */
+ /* self checksuming the gang block header */
+ ZIO_SET_CHECKSUM(&zc, DVA_GET_VDEV(dva),
+ dva_get_offset(dva, endian), bp->blk_birth, 0);
+ err = zio_checksum_verify(zc, ZIO_CHECKSUM_GANG_HEADER, endian,
+ (char *) zio_gb, SPA_GANGBLOCKSIZE);
+ if (err) {
+ free(zio_gb);
+ return err;
+ }
+
+ endian = (zfs_to_cpu64(bp->blk_prop, endian) >> 63) & 1;
+
+ for (i = 0; i < SPA_GBH_NBLKPTRS; i++) {
+ if (zio_gb->zg_blkptr[i].blk_birth == 0)
+ continue;
+
+ err = zio_read_data(&zio_gb->zg_blkptr[i], endian, buf, data);
+ if (err) {
+ free(zio_gb);
+ return err;
+ }
+ buf = (char *) buf + get_psize(&zio_gb->zg_blkptr[i], endian);
+ }
+ free(zio_gb);
+ return ZFS_ERR_NONE;
+}
+
+/*
+ * Read in a block of raw data to buf.
+ */
+static int
+zio_read_data(blkptr_t *bp, zfs_endian_t endian, void *buf,
+ struct zfs_data *data)
+{
+ int i, psize;
+ int err = ZFS_ERR_NONE;
+
+ psize = get_psize(bp, endian);
+
+ /* pick a good dva from the block pointer */
+ for (i = 0; i < SPA_DVAS_PER_BP; i++) {
+ uint64_t offset, sector;
+
+ if (bp->blk_dva[i].dva_word[0] == 0 && bp->blk_dva[i].dva_word[1] == 0)
+ continue;
+
+ if ((zfs_to_cpu64(bp->blk_dva[i].dva_word[1], endian)>>63) & 1) {
+ err = zio_read_gang(bp, endian, &bp->blk_dva[i], buf, data);
+ } else {
+ /* read in a data block */
+ offset = dva_get_offset(&bp->blk_dva[i], endian);
+ sector = DVA_OFFSET_TO_PHYS_SECTOR(offset);
+
+ err = zfs_devread(sector, 0, psize, buf);
+ }
+
+ if (!err) {
+ /*Check the underlying checksum before we rule this DVA as "good"*/
+ uint32_t checkalgo = (zfs_to_cpu64((bp)->blk_prop, endian) >> 40) & 0xff;
+
+ err = zio_checksum_verify(bp->blk_cksum, checkalgo, endian, buf, psize);
+ if (!err)
+ return ZFS_ERR_NONE;
+ }
+
+ /* If read failed or checksum bad, reset the error. Hopefully we've got some more DVA's to try.*/
+ }
+
+ if (!err) {
+ printf("couldn't find a valid DVA\n");
+ err = ZFS_ERR_BAD_FS;
+ }
+
+ return err;
+}
+
+/*
+ * Read in a block of data, verify its checksum, decompress if needed,
+ * and put the uncompressed data in buf.
+ */
+static int
+zio_read(blkptr_t *bp, zfs_endian_t endian, void **buf,
+ size_t *size, struct zfs_data *data)
+{
+ size_t lsize, psize;
+ unsigned int comp;
+ char *compbuf = NULL;
+ int err;
+
+ *buf = NULL;
+
+ comp = (zfs_to_cpu64((bp)->blk_prop, endian)>>32) & 0xff;
+ lsize = (BP_IS_HOLE(bp) ? 0 :
+ (((zfs_to_cpu64((bp)->blk_prop, endian) & 0xffff) + 1)
+ << SPA_MINBLOCKSHIFT));
+ psize = get_psize(bp, endian);
+
+ if (size)
+ *size = lsize;
+
+ if (comp >= ZIO_COMPRESS_FUNCTIONS) {
+ printf("compression algorithm %u not supported\n", (unsigned int) comp);
+ return ZFS_ERR_NOT_IMPLEMENTED_YET;
+ }
+
+ if (comp != ZIO_COMPRESS_OFF && decomp_table[comp].decomp_func == NULL) {
+ printf("compression algorithm %s not supported\n", decomp_table[comp].name);
+ return ZFS_ERR_NOT_IMPLEMENTED_YET;
+ }
+
+ if (comp != ZIO_COMPRESS_OFF) {
+ compbuf = malloc(psize);
+ if (!compbuf)
+ return ZFS_ERR_OUT_OF_MEMORY;
+ } else {
+ compbuf = *buf = malloc(lsize);
+ }
+
+ err = zio_read_data(bp, endian, compbuf, data);
+ if (err) {
+ free(compbuf);
+ *buf = NULL;
+ return err;
+ }
+
+ if (comp != ZIO_COMPRESS_OFF) {
+ *buf = malloc(lsize);
+ if (!*buf) {
+ free(compbuf);
+ return ZFS_ERR_OUT_OF_MEMORY;
+ }
+
+ err = decomp_table[comp].decomp_func(compbuf, *buf, psize, lsize);
+ free(compbuf);
+ if (err) {
+ free(*buf);
+ *buf = NULL;
+ return err;
+ }
+ }
+
+ return ZFS_ERR_NONE;
+}
+
+/*
+ * Get the block from a block id.
+ * push the block onto the stack.
+ *
+ */
+static int
+dmu_read(dnode_end_t *dn, uint64_t blkid, void **buf,
+ zfs_endian_t *endian_out, struct zfs_data *data)
+{
+ int idx, level;
+ blkptr_t *bp_array = dn->dn.dn_blkptr;
+ int epbs = dn->dn.dn_indblkshift - SPA_BLKPTRSHIFT;
+ blkptr_t *bp;
+ void *tmpbuf = 0;
+ zfs_endian_t endian;
+ int err = ZFS_ERR_NONE;
+
+ bp = malloc(sizeof(blkptr_t));
+ if (!bp)
+ return ZFS_ERR_OUT_OF_MEMORY;
+
+ endian = dn->endian;
+ for (level = dn->dn.dn_nlevels - 1; level >= 0; level--) {
+ idx = (blkid >> (epbs * level)) & ((1 << epbs) - 1);
+ *bp = bp_array[idx];
+ if (bp_array != dn->dn.dn_blkptr) {
+ free(bp_array);
+ bp_array = 0;
+ }
+
+ if (BP_IS_HOLE(bp)) {
+ size_t size = zfs_to_cpu16(dn->dn.dn_datablkszsec,
+ dn->endian)
+ << SPA_MINBLOCKSHIFT;
+ *buf = malloc(size);
+ if (*buf) {
+ err = ZFS_ERR_OUT_OF_MEMORY;
+ break;
+ }
+ memset(*buf, 0, size);
+ endian = (zfs_to_cpu64(bp->blk_prop, endian) >> 63) & 1;
+ break;
+ }
+ if (level == 0) {
+ err = zio_read(bp, endian, buf, 0, data);
+ endian = (zfs_to_cpu64(bp->blk_prop, endian) >> 63) & 1;
+ break;
+ }
+ err = zio_read(bp, endian, &tmpbuf, 0, data);
+ endian = (zfs_to_cpu64(bp->blk_prop, endian) >> 63) & 1;
+ if (err)
+ break;
+ bp_array = tmpbuf;
+ }
+ if (bp_array != dn->dn.dn_blkptr)
+ free(bp_array);
+ if (endian_out)
+ *endian_out = endian;
+
+ free(bp);
+ return err;
+}
+
+/*
+ * mzap_lookup: Looks up property described by "name" and returns the value
+ * in "value".
+ */
+static int
+mzap_lookup(mzap_phys_t *zapobj, zfs_endian_t endian,
+ int objsize, char *name, uint64_t * value)
+{
+ int i, chunks;
+ mzap_ent_phys_t *mzap_ent = zapobj->mz_chunk;
+
+ chunks = objsize / MZAP_ENT_LEN - 1;
+ for (i = 0; i < chunks; i++) {
+ if (strcmp(mzap_ent[i].mze_name, name) == 0) {
+ *value = zfs_to_cpu64(mzap_ent[i].mze_value, endian);
+ return ZFS_ERR_NONE;
+ }
+ }
+
+ printf("couldn't find '%s'\n", name);
+ return ZFS_ERR_FILE_NOT_FOUND;
+}
+
+static int
+mzap_iterate(mzap_phys_t *zapobj, zfs_endian_t endian, int objsize,
+ int (*hook)(const char *name,
+ uint64_t val,
+ struct zfs_data *data),
+ struct zfs_data *data)
+{
+ int i, chunks;
+ mzap_ent_phys_t *mzap_ent = zapobj->mz_chunk;
+
+ chunks = objsize / MZAP_ENT_LEN - 1;
+ for (i = 0; i < chunks; i++) {
+ if (hook(mzap_ent[i].mze_name,
+ zfs_to_cpu64(mzap_ent[i].mze_value, endian),
+ data))
+ return 1;
+ }
+
+ return 0;
+}
+
+static uint64_t
+zap_hash(uint64_t salt, const char *name)
+{
+ static uint64_t table[256];
+ const uint8_t *cp;
+ uint8_t c;
+ uint64_t crc = salt;
+
+ if (table[128] == 0) {
+ uint64_t *ct = NULL;
+ int i, j;
+ for (i = 0; i < 256; i++) {
+ for (ct = table + i, *ct = i, j = 8; j > 0; j--)
+ *ct = (*ct >> 1) ^ (-(*ct & 1) & ZFS_CRC64_POLY);
+ }
+ }
+
+ for (cp = (const uint8_t *) name; (c = *cp) != '\0'; cp++)
+ crc = (crc >> 8) ^ table[(crc ^ c) & 0xFF];
+
+ /*
+ * Only use 28 bits, since we need 4 bits in the cookie for the
+ * collision differentiator. We MUST use the high bits, since
+ * those are the onces that we first pay attention to when
+ * chosing the bucket.
+ */
+ crc &= ~((1ULL << (64 - ZAP_HASHBITS)) - 1);
+
+ return crc;
+}
+
+/*
+ * Only to be used on 8-bit arrays.
+ * array_len is actual len in bytes (not encoded le_value_length).
+ * buf is null-terminated.
+ */
+/* XXX */
+static int
+zap_leaf_array_equal(zap_leaf_phys_t *l, zfs_endian_t endian,
+ int blksft, int chunk, int array_len, const char *buf)
+{
+ int bseen = 0;
+
+ while (bseen < array_len) {
+ struct zap_leaf_array *la = &ZAP_LEAF_CHUNK(l, blksft, chunk).l_array;
+ int toread = min(array_len - bseen, ZAP_LEAF_ARRAY_BYTES);
+
+ if (chunk >= ZAP_LEAF_NUMCHUNKS(blksft))
+ return 0;
+
+ if (memcmp(la->la_array, buf + bseen, toread) != 0)
+ break;
+ chunk = zfs_to_cpu16(la->la_next, endian);
+ bseen += toread;
+ }
+ return (bseen == array_len);
+}
+
+/* XXX */
+static int
+zap_leaf_array_get(zap_leaf_phys_t *l, zfs_endian_t endian, int blksft,
+ int chunk, int array_len, char *buf)
+{
+ int bseen = 0;
+
+ while (bseen < array_len) {
+ struct zap_leaf_array *la = &ZAP_LEAF_CHUNK(l, blksft, chunk).l_array;
+ int toread = min(array_len - bseen, ZAP_LEAF_ARRAY_BYTES);
+
+ if (chunk >= ZAP_LEAF_NUMCHUNKS(blksft))
+ /* Don't use errno because this error is to be ignored. */
+ return ZFS_ERR_BAD_FS;
+
+ memcpy(buf + bseen, la->la_array, toread);
+ chunk = zfs_to_cpu16(la->la_next, endian);
+ bseen += toread;
+ }
+ return ZFS_ERR_NONE;
+}
+
+
+/*
+ * Given a zap_leaf_phys_t, walk thru the zap leaf chunks to get the
+ * value for the property "name".
+ *
+ */
+/* XXX */
+static int
+zap_leaf_lookup(zap_leaf_phys_t *l, zfs_endian_t endian,
+ int blksft, uint64_t h,
+ const char *name, uint64_t *value)
+{
+ uint16_t chunk;
+ struct zap_leaf_entry *le;
+
+ /* Verify if this is a valid leaf block */
+ if (zfs_to_cpu64(l->l_hdr.lh_block_type, endian) != ZBT_LEAF) {
+ printf("invalid leaf type\n");
+ return ZFS_ERR_BAD_FS;
+ }
+ if (zfs_to_cpu32(l->l_hdr.lh_magic, endian) != ZAP_LEAF_MAGIC) {
+ printf("invalid leaf magic\n");
+ return ZFS_ERR_BAD_FS;
+ }
+
+ for (chunk = zfs_to_cpu16(l->l_hash[LEAF_HASH(blksft, h)], endian);
+ chunk != CHAIN_END; chunk = le->le_next) {
+
+ if (chunk >= ZAP_LEAF_NUMCHUNKS(blksft)) {
+ printf("invalid chunk number\n");
+ return ZFS_ERR_BAD_FS;
+ }
+
+ le = ZAP_LEAF_ENTRY(l, blksft, chunk);
+
+ /* Verify the chunk entry */
+ if (le->le_type != ZAP_CHUNK_ENTRY) {
+ printf("invalid chunk entry\n");
+ return ZFS_ERR_BAD_FS;
+ }
+
+ if (zfs_to_cpu64(le->le_hash, endian) != h)
+ continue;
+
+ if (zap_leaf_array_equal(l, endian, blksft,
+ zfs_to_cpu16(le->le_name_chunk, endian),
+ zfs_to_cpu16(le->le_name_length, endian),
+ name)) {
+ struct zap_leaf_array *la;
+
+ if (le->le_int_size != 8 || le->le_value_length != 1) {
+ printf("invalid leaf chunk entry\n");
+ return ZFS_ERR_BAD_FS;
+ }
+ /* get the uint64_t property value */
+ la = &ZAP_LEAF_CHUNK(l, blksft, le->le_value_chunk).l_array;
+
+ *value = be64_to_cpu(la->la_array64);
+
+ return ZFS_ERR_NONE;
+ }
+ }
+
+ printf("couldn't find '%s'\n", name);
+ return ZFS_ERR_FILE_NOT_FOUND;
+}
+
+
+/* Verify if this is a fat zap header block */
+static int
+zap_verify(zap_phys_t *zap)
+{
+ if (zap->zap_magic != (uint64_t) ZAP_MAGIC) {
+ printf("bad ZAP magic\n");
+ return ZFS_ERR_BAD_FS;
+ }
+
+ if (zap->zap_flags != 0) {
+ printf("bad ZAP flags\n");
+ return ZFS_ERR_BAD_FS;
+ }
+
+ if (zap->zap_salt == 0) {
+ printf("bad ZAP salt\n");
+ return ZFS_ERR_BAD_FS;
+ }
+
+ return ZFS_ERR_NONE;
+}
+
+/*
+ * Fat ZAP lookup
+ *
+ */
+/* XXX */
+static int
+fzap_lookup(dnode_end_t *zap_dnode, zap_phys_t *zap,
+ char *name, uint64_t *value, struct zfs_data *data)
+{
+ void *l;
+ uint64_t hash, idx, blkid;
+ int blksft = zfs_log2(zfs_to_cpu16(zap_dnode->dn.dn_datablkszsec,
+ zap_dnode->endian) << DNODE_SHIFT);
+ int err;
+ zfs_endian_t leafendian;
+
+ err = zap_verify(zap);
+ if (err)
+ return err;
+
+ hash = zap_hash(zap->zap_salt, name);
+
+ /* get block id from index */
+ if (zap->zap_ptrtbl.zt_numblks != 0) {
+ printf("external pointer tables not supported\n");
+ return ZFS_ERR_NOT_IMPLEMENTED_YET;
+ }
+ idx = ZAP_HASH_IDX(hash, zap->zap_ptrtbl.zt_shift);
+ blkid = ((uint64_t *) zap)[idx + (1 << (blksft - 3 - 1))];
+
+ /* Get the leaf block */
+ if ((1U << blksft) < sizeof(zap_leaf_phys_t)) {
+ printf("ZAP leaf is too small\n");
+ return ZFS_ERR_BAD_FS;
+ }
+ err = dmu_read(zap_dnode, blkid, &l, &leafendian, data);
+ if (err)
+ return err;
+
+ err = zap_leaf_lookup(l, leafendian, blksft, hash, name, value);
+ free(l);
+ return err;
+}
+
+/* XXX */
+static int
+fzap_iterate(dnode_end_t *zap_dnode, zap_phys_t *zap,
+ int (*hook)(const char *name,
+ uint64_t val,
+ struct zfs_data *data),
+ struct zfs_data *data)
+{
+ zap_leaf_phys_t *l;
+ void *l_in;
+ uint64_t idx, blkid;
+ uint16_t chunk;
+ int blksft = zfs_log2(zfs_to_cpu16(zap_dnode->dn.dn_datablkszsec,
+ zap_dnode->endian) << DNODE_SHIFT);
+ int err;
+ zfs_endian_t endian;
+
+ if (zap_verify(zap))
+ return 0;
+
+ /* get block id from index */
+ if (zap->zap_ptrtbl.zt_numblks != 0) {
+ printf("external pointer tables not supported\n");
+ return 0;
+ }
+ /* Get the leaf block */
+ if ((1U << blksft) < sizeof(zap_leaf_phys_t)) {
+ printf("ZAP leaf is too small\n");
+ return 0;
+ }
+ for (idx = 0; idx < zap->zap_ptrtbl.zt_numblks; idx++) {
+ blkid = ((uint64_t *) zap)[idx + (1 << (blksft - 3 - 1))];
+
+ err = dmu_read(zap_dnode, blkid, &l_in, &endian, data);
+ l = l_in;
+ if (err)
+ continue;
+
+ /* Verify if this is a valid leaf block */
+ if (zfs_to_cpu64(l->l_hdr.lh_block_type, endian) != ZBT_LEAF) {
+ free(l);
+ continue;
+ }
+ if (zfs_to_cpu32(l->l_hdr.lh_magic, endian) != ZAP_LEAF_MAGIC) {
+ free(l);
+ continue;
+ }
+
+ for (chunk = 0; chunk < ZAP_LEAF_NUMCHUNKS(blksft); chunk++) {
+ char *buf;
+ struct zap_leaf_array *la;
+ struct zap_leaf_entry *le;
+ uint64_t val;
+ le = ZAP_LEAF_ENTRY(l, blksft, chunk);
+
+ /* Verify the chunk entry */
+ if (le->le_type != ZAP_CHUNK_ENTRY)
+ continue;
+
+ buf = malloc(zfs_to_cpu16(le->le_name_length, endian)
+ + 1);
+ if (zap_leaf_array_get(l, endian, blksft, le->le_name_chunk,
+ le->le_name_length, buf)) {
+ free(buf);
+ continue;
+ }
+ buf[le->le_name_length] = 0;
+
+ if (le->le_int_size != 8
+ || zfs_to_cpu16(le->le_value_length, endian) != 1)
+ continue;
+
+ /* get the uint64_t property value */
+ la = &ZAP_LEAF_CHUNK(l, blksft, le->le_value_chunk).l_array;
+ val = be64_to_cpu(la->la_array64);
+ if (hook(buf, val, data))
+ return 1;
+ free(buf);
+ }
+ }
+ return 0;
+}
+
+
+/*
+ * Read in the data of a zap object and find the value for a matching
+ * property name.
+ *
+ */
+static int
+zap_lookup(dnode_end_t *zap_dnode, char *name, uint64_t *val,
+ struct zfs_data *data)
+{
+ uint64_t block_type;
+ int size;
+ void *zapbuf;
+ int err;
+ zfs_endian_t endian;
+
+ /* Read in the first block of the zap object data. */
+ size = zfs_to_cpu16(zap_dnode->dn.dn_datablkszsec,
+ zap_dnode->endian) << SPA_MINBLOCKSHIFT;
+ err = dmu_read(zap_dnode, 0, &zapbuf, &endian, data);
+ if (err)
+ return err;
+ block_type = zfs_to_cpu64(*((uint64_t *) zapbuf), endian);
+
+ if (block_type == ZBT_MICRO) {
+ err = (mzap_lookup(zapbuf, endian, size, name, val));
+ free(zapbuf);
+ return err;
+ } else if (block_type == ZBT_HEADER) {
+ /* this is a fat zap */
+ err = (fzap_lookup(zap_dnode, zapbuf, name, val, data));
+ free(zapbuf);
+ return err;
+ }
+
+ printf("unknown ZAP type\n");
+ free(zapbuf);
+ return ZFS_ERR_BAD_FS;
+}
+
+static int
+zap_iterate(dnode_end_t *zap_dnode,
+ int (*hook)(const char *name, uint64_t val,
+ struct zfs_data *data),
+ struct zfs_data *data)
+{
+ uint64_t block_type;
+ int size;
+ void *zapbuf;
+ int err;
+ int ret;
+ zfs_endian_t endian;
+
+ /* Read in the first block of the zap object data. */
+ size = zfs_to_cpu16(zap_dnode->dn.dn_datablkszsec, zap_dnode->endian) << SPA_MINBLOCKSHIFT;
+ err = dmu_read(zap_dnode, 0, &zapbuf, &endian, data);
+ if (err)
+ return 0;
+ block_type = zfs_to_cpu64(*((uint64_t *) zapbuf), endian);
+
+ if (block_type == ZBT_MICRO) {
+ ret = mzap_iterate(zapbuf, endian, size, hook, data);
+ free(zapbuf);
+ return ret;
+ } else if (block_type == ZBT_HEADER) {
+ /* this is a fat zap */
+ ret = fzap_iterate(zap_dnode, zapbuf, hook, data);
+ free(zapbuf);
+ return ret;
+ }
+ printf("unknown ZAP type\n");
+ free(zapbuf);
+ return 0;
+}
+
+
+/*
+ * Get the dnode of an object number from the metadnode of an object set.
+ *
+ * Input
+ * mdn - metadnode to get the object dnode
+ * objnum - object number for the object dnode
+ * buf - data buffer that holds the returning dnode
+ */
+static int
+dnode_get(dnode_end_t *mdn, uint64_t objnum, uint8_t type,
+ dnode_end_t *buf, struct zfs_data *data)
+{
+ uint64_t blkid, blksz; /* the block id this object dnode is in */
+ int epbs; /* shift of number of dnodes in a block */
+ int idx; /* index within a block */
+ void *dnbuf;
+ int err;
+ zfs_endian_t endian;
+
+ blksz = zfs_to_cpu16(mdn->dn.dn_datablkszsec,
+ mdn->endian) << SPA_MINBLOCKSHIFT;
+
+ epbs = zfs_log2(blksz) - DNODE_SHIFT;
+ blkid = objnum >> epbs;
+ idx = objnum & ((1 << epbs) - 1);
+
+ if (data->dnode_buf != NULL && memcmp(data->dnode_mdn, mdn,
+ sizeof(*mdn)) == 0
+ && objnum >= data->dnode_start && objnum < data->dnode_end) {
+ memmove(&(buf->dn), &(data->dnode_buf)[idx], DNODE_SIZE);
+ buf->endian = data->dnode_endian;
+ if (type && buf->dn.dn_type != type) {
+ printf("incorrect dnode type: %02X != %02x\n", buf->dn.dn_type, type);
+ return ZFS_ERR_BAD_FS;
+ }
+ return ZFS_ERR_NONE;
+ }
+
+ err = dmu_read(mdn, blkid, &dnbuf, &endian, data);
+ if (err)
+ return err;
+
+ free(data->dnode_buf);
+ free(data->dnode_mdn);
+ data->dnode_mdn = malloc(sizeof(*mdn));
+ if (!data->dnode_mdn) {
+ data->dnode_buf = 0;
+ } else {
+ memcpy(data->dnode_mdn, mdn, sizeof(*mdn));
+ data->dnode_buf = dnbuf;
+ data->dnode_start = blkid << epbs;
+ data->dnode_end = (blkid + 1) << epbs;
+ data->dnode_endian = endian;
+ }
+
+ memmove(&(buf->dn), (dnode_phys_t *) dnbuf + idx, DNODE_SIZE);
+ buf->endian = endian;
+ if (type && buf->dn.dn_type != type) {
+ printf("incorrect dnode type\n");
+ return ZFS_ERR_BAD_FS;
+ }
+
+ return ZFS_ERR_NONE;
+}
+
+/*
+ * Get the file dnode for a given file name where mdn is the meta dnode
+ * for this ZFS object set. When found, place the file dnode in dn.
+ * The 'path' argument will be mangled.
+ *
+ */
+static int
+dnode_get_path(dnode_end_t *mdn, const char *path_in, dnode_end_t *dn,
+ struct zfs_data *data)
+{
+ uint64_t objnum, version;
+ char *cname, ch;
+ int err = ZFS_ERR_NONE;
+ char *path, *path_buf;
+ struct dnode_chain {
+ struct dnode_chain *next;
+ dnode_end_t dn;
+ };
+ struct dnode_chain *dnode_path = 0, *dn_new, *root;
+
+ dn_new = malloc(sizeof(*dn_new));
+ if (!dn_new)
+ return ZFS_ERR_OUT_OF_MEMORY;
+ dn_new->next = 0;
+ dnode_path = root = dn_new;
+
+ err = dnode_get(mdn, MASTER_NODE_OBJ, DMU_OT_MASTER_NODE,
+ &(dnode_path->dn), data);
+ if (err) {
+ free(dn_new);
+ return err;
+ }
+
+ err = zap_lookup(&(dnode_path->dn), ZPL_VERSION_STR, &version, data);
+ if (err) {
+ free(dn_new);
+ return err;
+ }
+ if (version > ZPL_VERSION) {
+ free(dn_new);
+ printf("too new ZPL version\n");
+ return ZFS_ERR_NOT_IMPLEMENTED_YET;
+ }
+
+ err = zap_lookup(&(dnode_path->dn), ZFS_ROOT_OBJ, &objnum, data);
+ if (err) {
+ free(dn_new);
+ return err;
+ }
+
+ err = dnode_get(mdn, objnum, 0, &(dnode_path->dn), data);
+ if (err) {
+ free(dn_new);
+ return err;
+ }
+
+ path = path_buf = strdup(path_in);
+ if (!path_buf) {
+ free(dn_new);
+ return ZFS_ERR_OUT_OF_MEMORY;
+ }
+
+ while (1) {
+ /* skip leading slashes */
+ while (*path == '/')
+ path++;
+ if (!*path)
+ break;
+ /* get the next component name */
+ cname = path;
+ while (*path && *path != '/')
+ path++;
+ /* Skip dot. */
+ if (cname + 1 == path && cname[0] == '.')
+ continue;
+ /* Handle double dot. */
+ if (cname + 2 == path && cname[0] == '.' && cname[1] == '.') {
+ if (dn_new->next) {
+ dn_new = dnode_path;
+ dnode_path = dn_new->next;
+ free(dn_new);
+ } else {
+ printf("can't resolve ..\n");
+ err = ZFS_ERR_FILE_NOT_FOUND;
+ break;
+ }
+ continue;
+ }
+
+ ch = *path;
+ *path = 0; /* ensure null termination */
+
+ if (dnode_path->dn.dn.dn_type != DMU_OT_DIRECTORY_CONTENTS) {
+ free(path_buf);
+ printf("not a directory\n");
+ return ZFS_ERR_BAD_FILE_TYPE;
+ }
+ err = zap_lookup(&(dnode_path->dn), cname, &objnum, data);
+ if (err)
+ break;
+
+ dn_new = malloc(sizeof(*dn_new));
+ if (!dn_new) {
+ err = ZFS_ERR_OUT_OF_MEMORY;
+ break;
+ }
+ dn_new->next = dnode_path;
+ dnode_path = dn_new;
+
+ objnum = ZFS_DIRENT_OBJ(objnum);
+ err = dnode_get(mdn, objnum, 0, &(dnode_path->dn), data);
+ if (err)
+ break;
+
+ *path = ch;
+ }
+
+ if (!err)
+ memcpy(dn, &(dnode_path->dn), sizeof(*dn));
+
+ while (dnode_path) {
+ dn_new = dnode_path->next;
+ free(dnode_path);
+ dnode_path = dn_new;
+ }
+ free(path_buf);
+ return err;
+}
+
+
+/*
+ * Given a MOS metadnode, get the metadnode of a given filesystem name (fsname),
+ * e.g. pool/rootfs, or a given object number (obj), e.g. the object number
+ * of pool/rootfs.
+ *
+ * If no fsname and no obj are given, return the DSL_DIR metadnode.
+ * If fsname is given, return its metadnode and its matching object number.
+ * If only obj is given, return the metadnode for this object number.
+ *
+ */
+static int
+get_filesystem_dnode(dnode_end_t *mosmdn, char *fsname,
+ dnode_end_t *mdn, struct zfs_data *data)
+{
+ uint64_t objnum;
+ int err;
+
+ err = dnode_get(mosmdn, DMU_POOL_DIRECTORY_OBJECT,
+ DMU_OT_OBJECT_DIRECTORY, mdn, data);
+ if (err)
+ return err;
+
+ err = zap_lookup(mdn, DMU_POOL_ROOT_DATASET, &objnum, data);
+ if (err)
+ return err;
+
+ err = dnode_get(mosmdn, objnum, DMU_OT_DSL_DIR, mdn, data);
+ if (err)
+ return err;
+
+ while (*fsname) {
+ uint64_t childobj;
+ char *cname, ch;
+
+ while (*fsname == '/')
+ fsname++;
+
+ if (!*fsname || *fsname == '@')
+ break;
+
+ cname = fsname;
+ while (*fsname && !isspace(*fsname) && *fsname != '/')
+ fsname++;
+ ch = *fsname;
+ *fsname = 0;
+
+ childobj = zfs_to_cpu64((((dsl_dir_phys_t *) DN_BONUS(&mdn->dn)))->dd_child_dir_zapobj, mdn->endian);
+ err = dnode_get(mosmdn, childobj,
+ DMU_OT_DSL_DIR_CHILD_MAP, mdn, data);
+ if (err)
+ return err;
+
+ err = zap_lookup(mdn, cname, &objnum, data);
+ if (err)
+ return err;
+
+ err = dnode_get(mosmdn, objnum, DMU_OT_DSL_DIR, mdn, data);
+ if (err)
+ return err;
+
+ *fsname = ch;
+ }
+ return ZFS_ERR_NONE;
+}
+
+static int
+make_mdn(dnode_end_t *mdn, struct zfs_data *data)
+{
+ void *osp;
+ blkptr_t *bp;
+ size_t ospsize;
+ int err;
+
+ bp = &(((dsl_dataset_phys_t *) DN_BONUS(&mdn->dn))->ds_bp);
+ err = zio_read(bp, mdn->endian, &osp, &ospsize, data);
+ if (err)
+ return err;
+ if (ospsize < OBJSET_PHYS_SIZE_V14) {
+ free(osp);
+ printf("too small osp\n");
+ return ZFS_ERR_BAD_FS;
+ }
+
+ mdn->endian = (zfs_to_cpu64(bp->blk_prop, mdn->endian)>>63) & 1;
+ memmove((char *) &(mdn->dn),
+ (char *) &((objset_phys_t *) osp)->os_meta_dnode, DNODE_SIZE);
+ free(osp);
+ return ZFS_ERR_NONE;
+}
+
+static int
+dnode_get_fullpath(const char *fullpath, dnode_end_t *mdn,
+ uint64_t *mdnobj, dnode_end_t *dn, int *isfs,
+ struct zfs_data *data)
+{
+ char *fsname, *snapname;
+ const char *ptr_at, *filename;
+ uint64_t headobj;
+ int err;
+
+ ptr_at = strchr(fullpath, '@');
+ if (!ptr_at) {
+ *isfs = 1;
+ filename = 0;
+ snapname = 0;
+ fsname = strdup(fullpath);
+ } else {
+ const char *ptr_slash = strchr(ptr_at, '/');
+
+ *isfs = 0;
+ fsname = malloc(ptr_at - fullpath + 1);
+ if (!fsname)
+ return ZFS_ERR_OUT_OF_MEMORY;
+ memcpy(fsname, fullpath, ptr_at - fullpath);
+ fsname[ptr_at - fullpath] = 0;
+ if (ptr_at[1] && ptr_at[1] != '/') {
+ snapname = malloc(ptr_slash - ptr_at);
+ if (!snapname) {
+ free(fsname);
+ return ZFS_ERR_OUT_OF_MEMORY;
+ }
+ memcpy(snapname, ptr_at + 1, ptr_slash - ptr_at - 1);
+ snapname[ptr_slash - ptr_at - 1] = 0;
+ } else {
+ snapname = 0;
+ }
+ if (ptr_slash)
+ filename = ptr_slash;
+ else
+ filename = "/";
+ printf("zfs fsname = '%s' snapname='%s' filename = '%s'\n",
+ fsname, snapname, filename);
+ }
+
+
+ err = get_filesystem_dnode(&(data->mos), fsname, dn, data);
+
+ if (err) {
+ free(fsname);
+ free(snapname);
+ return err;
+ }
+
+ headobj = zfs_to_cpu64(((dsl_dir_phys_t *) DN_BONUS(&dn->dn))->dd_head_dataset_obj, dn->endian);
+
+ err = dnode_get(&(data->mos), headobj, DMU_OT_DSL_DATASET, mdn, data);
+ if (err) {
+ free(fsname);
+ free(snapname);
+ return err;
+ }
+
+ if (snapname) {
+ uint64_t snapobj;
+
+ snapobj = zfs_to_cpu64(((dsl_dataset_phys_t *) DN_BONUS(&mdn->dn))->ds_snapnames_zapobj, mdn->endian);
+
+ err = dnode_get(&(data->mos), snapobj,
+ DMU_OT_DSL_DS_SNAP_MAP, mdn, data);
+ if (!err)
+ err = zap_lookup(mdn, snapname, &headobj, data);
+ if (!err)
+ err = dnode_get(&(data->mos), headobj, DMU_OT_DSL_DATASET, mdn, data);
+ if (err) {
+ free(fsname);
+ free(snapname);
+ return err;
+ }
+ }
+
+ if (mdnobj)
+ *mdnobj = headobj;
+
+ make_mdn(mdn, data);
+
+ if (*isfs) {
+ free(fsname);
+ free(snapname);
+ return ZFS_ERR_NONE;
+ }
+ err = dnode_get_path(mdn, filename, dn, data);
+ free(fsname);
+ free(snapname);
+ return err;
+}
+
+/*
+ * For a given XDR packed nvlist, verify the first 4 bytes and move on.
+ *
+ * An XDR packed nvlist is encoded as (comments from nvs_xdr_create) :
+ *
+ * encoding method/host endian (4 bytes)
+ * nvl_version (4 bytes)
+ * nvl_nvflag (4 bytes)
+ * encoded nvpairs:
+ * encoded size of the nvpair (4 bytes)
+ * decoded size of the nvpair (4 bytes)
+ * name string size (4 bytes)
+ * name string data (sizeof(NV_ALIGN4(string))
+ * data type (4 bytes)
+ * # of elements in the nvpair (4 bytes)
+ * data
+ * 2 zero's for the last nvpair
+ * (end of the entire list) (8 bytes)
+ *
+ */
+
+static int
+nvlist_find_value(char *nvlist, char *name, int valtype, char **val,
+ size_t *size_out, size_t *nelm_out)
+{
+ int name_len, type, encode_size;
+ char *nvpair, *nvp_name;
+
+ /* Verify if the 1st and 2nd byte in the nvlist are valid. */
+ /* NOTE: independently of what endianness header announces all
+ subsequent values are big-endian. */
+ if (nvlist[0] != NV_ENCODE_XDR || (nvlist[1] != NV_LITTLE_ENDIAN
+ && nvlist[1] != NV_BIG_ENDIAN)) {
+ printf("zfs incorrect nvlist header\n");
+ return ZFS_ERR_BAD_FS;
+ }
+
+ /* skip the header, nvl_version, and nvl_nvflag */
+ nvlist = nvlist + 4 * 3;
+ /*
+ * Loop thru the nvpair list
+ * The XDR representation of an integer is in big-endian byte order.
+ */
+ while ((encode_size = be32_to_cpu(*(uint32_t *) nvlist))) {
+ int nelm;
+
+ nvpair = nvlist + 4 * 2; /* skip the encode/decode size */
+
+ name_len = be32_to_cpu(*(uint32_t *) nvpair);
+ nvpair += 4;
+
+ nvp_name = nvpair;
+ nvpair = nvpair + ((name_len + 3) & ~3); /* align */
+
+ type = be32_to_cpu(*(uint32_t *) nvpair);
+ nvpair += 4;
+
+ nelm = be32_to_cpu(*(uint32_t *) nvpair);
+ if (nelm < 1) {
+ printf("empty nvpair\n");
+ return ZFS_ERR_BAD_FS;
+ }
+
+ nvpair += 4;
+
+ if ((strncmp(nvp_name, name, name_len) == 0) && type == valtype) {
+ *val = nvpair;
+ *size_out = encode_size;
+ if (nelm_out)
+ *nelm_out = nelm;
+ return 1;
+ }
+
+ nvlist += encode_size; /* goto the next nvpair */
+ }
+ return 0;
+}
+
+int
+zfs_nvlist_lookup_uint64(char *nvlist, char *name, uint64_t *out)
+{
+ char *nvpair;
+ size_t size;
+ int found;
+
+ found = nvlist_find_value(nvlist, name, DATA_TYPE_UINT64, &nvpair, &size, 0);
+ if (!found)
+ return 0;
+ if (size < sizeof(uint64_t)) {
+ printf("invalid uint64\n");
+ return ZFS_ERR_BAD_FS;
+ }
+
+ *out = be64_to_cpu(*(uint64_t *) nvpair);
+ return 1;
+}
+
+char *
+zfs_nvlist_lookup_string(char *nvlist, char *name)
+{
+ char *nvpair;
+ char *ret;
+ size_t slen;
+ size_t size;
+ int found;
+
+ found = nvlist_find_value(nvlist, name, DATA_TYPE_STRING, &nvpair, &size, 0);
+ if (!found)
+ return 0;
+ if (size < 4) {
+ printf("invalid string\n");
+ return 0;
+ }
+ slen = be32_to_cpu(*(uint32_t *) nvpair);
+ if (slen > size - 4)
+ slen = size - 4;
+ ret = malloc(slen + 1);
+ if (!ret)
+ return 0;
+ memcpy(ret, nvpair + 4, slen);
+ ret[slen] = 0;
+ return ret;
+}
+
+char *
+zfs_nvlist_lookup_nvlist(char *nvlist, char *name)
+{
+ char *nvpair;
+ char *ret;
+ size_t size;
+ int found;
+
+ found = nvlist_find_value(nvlist, name, DATA_TYPE_NVLIST, &nvpair,
+ &size, 0);
+ if (!found)
+ return 0;
+ ret = calloc(1, size + 3 * sizeof(uint32_t));
+ if (!ret)
+ return 0;
+ memcpy(ret, nvlist, sizeof(uint32_t));
+
+ memcpy(ret + sizeof(uint32_t), nvpair, size);
+ return ret;
+}
+
+int
+zfs_nvlist_lookup_nvlist_array_get_nelm(char *nvlist, char *name)
+{
+ char *nvpair;
+ size_t nelm, size;
+ int found;
+
+ found = nvlist_find_value(nvlist, name, DATA_TYPE_NVLIST, &nvpair,
+ &size, &nelm);
+ if (!found)
+ return -1;
+ return nelm;
+}
+
+char *
+zfs_nvlist_lookup_nvlist_array(char *nvlist, char *name,
+ size_t index)
+{
+ char *nvpair, *nvpairptr;
+ int found;
+ char *ret;
+ size_t size;
+ unsigned i;
+ size_t nelm;
+
+ found = nvlist_find_value(nvlist, name, DATA_TYPE_NVLIST, &nvpair,
+ &size, &nelm);
+ if (!found)
+ return 0;
+ if (index >= nelm) {
+ printf("trying to lookup past nvlist array\n");
+ return 0;
+ }
+
+ nvpairptr = nvpair;
+
+ for (i = 0; i < index; i++) {
+ uint32_t encode_size;
+
+ /* skip the header, nvl_version, and nvl_nvflag */
+ nvpairptr = nvpairptr + 4 * 2;
+
+ while (nvpairptr < nvpair + size
+ && (encode_size = be32_to_cpu(*(uint32_t *) nvpairptr)))
+ nvlist += encode_size; /* goto the next nvpair */
+
+ nvlist = nvlist + 4 * 2; /* skip the ending 2 zeros - 8 bytes */
+ }
+
+ if (nvpairptr >= nvpair + size
+ || nvpairptr + be32_to_cpu(*(uint32_t *) (nvpairptr + 4 * 2))
+ >= nvpair + size) {
+ printf("incorrect nvlist array\n");
+ return 0;
+ }
+
+ ret = calloc(1, be32_to_cpu(*(uint32_t *) (nvpairptr + 4 * 2))
+ + 3 * sizeof(uint32_t));
+ if (!ret)
+ return 0;
+ memcpy(ret, nvlist, sizeof(uint32_t));
+
+ memcpy(ret + sizeof(uint32_t), nvpairptr, size);
+ return ret;
+}
+
+static int
+int_zfs_fetch_nvlist(struct zfs_data *data, char **nvlist)
+{
+ int err;
+
+ *nvlist = malloc(VDEV_PHYS_SIZE);
+ /* Read in the vdev name-value pair list (112K). */
+ err = zfs_devread(data->vdev_phys_sector, 0, VDEV_PHYS_SIZE, *nvlist);
+ if (err) {
+ free(*nvlist);
+ *nvlist = 0;
+ return err;
+ }
+ return ZFS_ERR_NONE;
+}
+
+/*
+ * Check the disk label information and retrieve needed vdev name-value pairs.
+ *
+ */
+static int
+check_pool_label(struct zfs_data *data)
+{
+ uint64_t pool_state;
+ char *nvlist; /* for the pool */
+ char *vdevnvlist; /* for the vdev */
+ uint64_t diskguid;
+ uint64_t version;
+ int found;
+ int err;
+
+ err = int_zfs_fetch_nvlist(data, &nvlist);
+ if (err)
+ return err;
+
+ found = zfs_nvlist_lookup_uint64(nvlist, ZPOOL_CONFIG_POOL_STATE,
+ &pool_state);
+ if (!found) {
+ free(nvlist);
+ printf("zfs pool state not found\n");
+ return ZFS_ERR_BAD_FS;
+ }
+
+ if (pool_state == POOL_STATE_DESTROYED) {
+ free(nvlist);
+ printf("zpool is marked as destroyed\n");
+ return ZFS_ERR_BAD_FS;
+ }
+
+ data->label_txg = 0;
+ found = zfs_nvlist_lookup_uint64(nvlist, ZPOOL_CONFIG_POOL_TXG,
+ &data->label_txg);
+ if (!found) {
+ free(nvlist);
+ printf("zfs pool txg not found\n");
+ return ZFS_ERR_BAD_FS;
+ }
+
+ /* not an active device */
+ if (data->label_txg == 0) {
+ free(nvlist);
+ printf("zpool is not active\n");
+ return ZFS_ERR_BAD_FS;
+ }
+
+ found = zfs_nvlist_lookup_uint64(nvlist, ZPOOL_CONFIG_VERSION,
+ &version);
+ if (!found) {
+ free(nvlist);
+ printf("zpool config version not found\n");
+ return ZFS_ERR_BAD_FS;
+ }
+
+ if (version > SPA_VERSION) {
+ free(nvlist);
+ printf("SPA version too new %llu > %llu\n",
+ (unsigned long long) version,
+ (unsigned long long) SPA_VERSION);
+ return ZFS_ERR_NOT_IMPLEMENTED_YET;
+ }
+
+ vdevnvlist = zfs_nvlist_lookup_nvlist(nvlist, ZPOOL_CONFIG_VDEV_TREE);
+ if (!vdevnvlist) {
+ free(nvlist);
+ printf("ZFS config vdev tree not found\n");
+ return ZFS_ERR_BAD_FS;
+ }
+
+ found = zfs_nvlist_lookup_uint64(vdevnvlist, ZPOOL_CONFIG_ASHIFT,
+ &data->vdev_ashift);
+ free(vdevnvlist);
+ if (!found) {
+ free(nvlist);
+ printf("ZPOOL config ashift not found\n");
+ return ZFS_ERR_BAD_FS;
+ }
+
+ found = zfs_nvlist_lookup_uint64(nvlist, ZPOOL_CONFIG_GUID, &diskguid);
+ if (!found) {
+ free(nvlist);
+ printf("ZPOOL config guid not found\n");
+ return ZFS_ERR_BAD_FS;
+ }
+
+ found = zfs_nvlist_lookup_uint64(nvlist, ZPOOL_CONFIG_POOL_GUID, &data->pool_guid);
+ if (!found) {
+ free(nvlist);
+ printf("ZPOOL config pool guid not found\n");
+ return ZFS_ERR_BAD_FS;
+ }
+
+ free(nvlist);
+
+ printf("ZFS Pool GUID: %llu (%016llx) Label: GUID: %llu (%016llx), txg: %llu, SPA v%llu, ashift: %llu\n",
+ (unsigned long long) data->pool_guid,
+ (unsigned long long) data->pool_guid,
+ (unsigned long long) diskguid,
+ (unsigned long long) diskguid,
+ (unsigned long long) data->label_txg,
+ (unsigned long long) version,
+ (unsigned long long) data->vdev_ashift);
+
+ return ZFS_ERR_NONE;
+}
+
+/*
+ * vdev_label_start returns the physical disk offset (in bytes) of
+ * label "l".
+ */
+static uint64_t vdev_label_start(uint64_t psize, int l)
+{
+ return (l * sizeof(vdev_label_t) + (l < VDEV_LABELS / 2 ?
+ 0 : psize -
+ VDEV_LABELS * sizeof(vdev_label_t)));
+}
+
+void
+zfs_unmount(struct zfs_data *data)
+{
+ free(data->dnode_buf);
+ free(data->dnode_mdn);
+ free(data->file_buf);
+ free(data);
+}
+
+/*
+ * zfs_mount() locates a valid uberblock of the root pool and read in its MOS
+ * to the memory address MOS.
+ *
+ */
+struct zfs_data *
+zfs_mount(device_t dev)
+{
+ struct zfs_data *data = 0;
+ int label = 0, bestlabel = -1;
+ char *ub_array;
+ uberblock_t *ubbest;
+ uberblock_t *ubcur = NULL;
+ void *osp = 0;
+ size_t ospsize;
+ int err;
+
+ data = malloc(sizeof(*data));
+ if (!data)
+ return 0;
+ memset(data, 0, sizeof(*data));
+
+ ub_array = malloc(VDEV_UBERBLOCK_RING);
+ if (!ub_array) {
+ zfs_unmount(data);
+ return 0;
+ }
+
+ ubbest = malloc(sizeof(*ubbest));
+ if (!ubbest) {
+ free(ub_array);
+ zfs_unmount(data);
+ return 0;
+ }
+ memset(ubbest, 0, sizeof(*ubbest));
+
+ /*
+ * some eltorito stacks don't give us a size and
+ * we end up setting the size to MAXUINT, further
+ * some of these devices stop working once a single
+ * read past the end has been issued. Checking
+ * for a maximum part_length and skipping the backup
+ * labels at the end of the slice/partition/device
+ * avoids breaking down on such devices.
+ */
+ const int vdevnum =
+ dev->part_length == 0 ?
+ VDEV_LABELS / 2 : VDEV_LABELS;
+
+ /* Size in bytes of the device (disk or partition) aligned to label size*/
+ uint64_t device_size =
+ dev->part_length << SECTOR_BITS;
+
+ const uint64_t alignedbytes =
+ P2ALIGN(device_size, (uint64_t) sizeof(vdev_label_t));
+
+ for (label = 0; label < vdevnum; label++) {
+ uint64_t labelstartbytes = vdev_label_start(alignedbytes, label);
+ uint64_t labelstart = labelstartbytes >> SECTOR_BITS;
+
+ debug("zfs reading label %d at sector %llu (byte %llu)\n",
+ label, (unsigned long long) labelstart,
+ (unsigned long long) labelstartbytes);
+
+ data->vdev_phys_sector = labelstart +
+ ((VDEV_SKIP_SIZE + VDEV_BOOT_HEADER_SIZE) >> SECTOR_BITS);
+
+ err = check_pool_label(data);
+ if (err) {
+ printf("zfs error checking label %d\n", label);
+ continue;
+ }
+
+ /* Read in the uberblock ring (128K). */
+ err = zfs_devread(data->vdev_phys_sector +
+ (VDEV_PHYS_SIZE >> SECTOR_BITS),
+ 0, VDEV_UBERBLOCK_RING, ub_array);
+ if (err) {
+ printf("zfs error reading uberblock ring for label %d\n", label);
+ continue;
+ }
+
+ ubcur = find_bestub(ub_array, data);
+ if (!ubcur) {
+ printf("zfs No good uberblocks found in label %d\n", label);
+ continue;
+ }
+
+ if (vdev_uberblock_compare(ubcur, ubbest) > 0) {
+ /* Looks like the block is good, so use it.*/
+ memcpy(ubbest, ubcur, sizeof(*ubbest));
+ bestlabel = label;
+ debug("zfs Current best uberblock found in label %d\n", label);
+ }
+ }
+ free(ub_array);
+
+ /* We zero'd the structure to begin with. If we never assigned to it,
+ magic will still be zero. */
+ if (!ubbest->ub_magic) {
+ printf("couldn't find a valid ZFS label\n");
+ zfs_unmount(data);
+ free(ubbest);
+ return 0;
+ }
+
+ debug("zfs ubbest %p in label %d\n", ubbest, bestlabel);
+
+ zfs_endian_t ub_endian =
+ zfs_to_cpu64(ubbest->ub_magic, LITTLE_ENDIAN) == UBERBLOCK_MAGIC
+ ? LITTLE_ENDIAN : BIG_ENDIAN;
+
+ debug("zfs endian set to %s\n", !ub_endian ? "big" : "little");
+
+ err = zio_read(&ubbest->ub_rootbp, ub_endian, &osp, &ospsize, data);
+
+ if (err) {
+ printf("couldn't zio_read object directory\n");
+ zfs_unmount(data);
+ free(osp);
+ free(ubbest);
+ return 0;
+ }
+
+ if (ospsize < OBJSET_PHYS_SIZE_V14) {
+ printf("osp too small\n");
+ zfs_unmount(data);
+ free(osp);
+ free(ubbest);
+ return 0;
+ }
+
+ /* Got the MOS. Save it at the memory addr MOS. */
+ memmove(&(data->mos.dn), &((objset_phys_t *) osp)->os_meta_dnode, DNODE_SIZE);
+ data->mos.endian =
+ (zfs_to_cpu64(ubbest->ub_rootbp.blk_prop, ub_endian) >> 63) & 1;
+ memmove(&(data->current_uberblock), ubbest, sizeof(uberblock_t));
+
+ free(osp);
+ free(ubbest);
+
+ return data;
+}
+
+int
+zfs_fetch_nvlist(device_t dev, char **nvlist)
+{
+ struct zfs_data *zfs;
+ int err;
+
+ zfs = zfs_mount(dev);
+ if (!zfs)
+ return ZFS_ERR_BAD_FS;
+ err = int_zfs_fetch_nvlist(zfs, nvlist);
+ zfs_unmount(zfs);
+ return err;
+}
+
+/*
+ * zfs_open() locates a file in the rootpool by following the
+ * MOS and places the dnode of the file in the memory address DNODE.
+ */
+int
+zfs_open(struct zfs_file *file, const char *fsfilename)
+{
+ struct zfs_data *data;
+ int err;
+ int isfs;
+
+ data = zfs_mount(file->device);
+ if (!data)
+ return ZFS_ERR_BAD_FS;
+
+ err = dnode_get_fullpath(fsfilename, &(data->mdn), 0,
+ &(data->dnode), &isfs, data);
+ if (err) {
+ zfs_unmount(data);
+ return err;
+ }
+
+ if (isfs) {
+ zfs_unmount(data);
+ printf("Missing @ or / separator\n");
+ return ZFS_ERR_FILE_NOT_FOUND;
+ }
+
+ /* We found the dnode for this file. Verify if it is a plain file. */
+ if (data->dnode.dn.dn_type != DMU_OT_PLAIN_FILE_CONTENTS) {
+ zfs_unmount(data);
+ printf("not a file\n");
+ return ZFS_ERR_BAD_FILE_TYPE;
+ }
+
+ /* get the file size and set the file position to 0 */
+
+ /*
+ * For DMU_OT_SA we will need to locate the SIZE attribute
+ * attribute, which could be either in the bonus buffer
+ * or the "spill" block.
+ */
+ if (data->dnode.dn.dn_bonustype == DMU_OT_SA) {
+ void *sahdrp;
+ int hdrsize;
+
+ if (data->dnode.dn.dn_bonuslen != 0) {
+ sahdrp = (sa_hdr_phys_t *) DN_BONUS(&data->dnode.dn);
+ } else if (data->dnode.dn.dn_flags & DNODE_FLAG_SPILL_BLKPTR) {
+ blkptr_t *bp = &data->dnode.dn.dn_spill;
+
+ err = zio_read(bp, data->dnode.endian, &sahdrp, NULL, data);
+ if (err)
+ return err;
+ } else {
+ printf("filesystem is corrupt :(\n");
+ return ZFS_ERR_BAD_FS;
+ }
+
+ hdrsize = SA_HDR_SIZE(((sa_hdr_phys_t *) sahdrp));
+ file->size = *(uint64_t *) ((char *) sahdrp + hdrsize + SA_SIZE_OFFSET);
+ if ((data->dnode.dn.dn_bonuslen == 0) &&
+ (data->dnode.dn.dn_flags & DNODE_FLAG_SPILL_BLKPTR))
+ free(sahdrp);
+ } else {
+ file->size = zfs_to_cpu64(((znode_phys_t *) DN_BONUS(&data->dnode.dn))->zp_size, data->dnode.endian);
+ }
+
+ file->data = data;
+ file->offset = 0;
+
+ return ZFS_ERR_NONE;
+}
+
+uint64_t
+zfs_read(zfs_file_t file, char *buf, uint64_t len)
+{
+ struct zfs_data *data = (struct zfs_data *) file->data;
+ int blksz, movesize;
+ uint64_t length;
+ int64_t red;
+ int err;
+
+ if (data->file_buf == NULL) {
+ data->file_buf = malloc(SPA_MAXBLOCKSIZE);
+ if (!data->file_buf)
+ return -1;
+ data->file_start = data->file_end = 0;
+ }
+
+ /*
+ * If offset is in memory, move it into the buffer provided and return.
+ */
+ if (file->offset >= data->file_start
+ && file->offset + len <= data->file_end) {
+ memmove(buf, data->file_buf + file->offset - data->file_start,
+ len);
+ return len;
+ }
+
+ blksz = zfs_to_cpu16(data->dnode.dn.dn_datablkszsec,
+ data->dnode.endian) << SPA_MINBLOCKSHIFT;
+
+ /*
+ * Entire Dnode is too big to fit into the space available. We
+ * will need to read it in chunks. This could be optimized to
+ * read in as large a chunk as there is space available, but for
+ * now, this only reads in one data block at a time.
+ */
+ length = len;
+ red = 0;
+ while (length) {
+ void *t;
+ /*
+ * Find requested blkid and the offset within that block.
+ */
+ uint64_t blkid = file->offset + red;
+ blkid = do_div(blkid, blksz);
+ free(data->file_buf);
+ data->file_buf = 0;
+
+ err = dmu_read(&(data->dnode), blkid, &t,
+ 0, data);
+ data->file_buf = t;
+ if (err)
+ return -1;
+
+ data->file_start = blkid * blksz;
+ data->file_end = data->file_start + blksz;
+
+ movesize = min(length, data->file_end - (int)file->offset - red);
+
+ memmove(buf, data->file_buf + file->offset + red
+ - data->file_start, movesize);
+ buf += movesize;
+ length -= movesize;
+ red += movesize;
+ }
+
+ return len;
+}
+
+int
+zfs_close(zfs_file_t file)
+{
+ zfs_unmount((struct zfs_data *) file->data);
+ return ZFS_ERR_NONE;
+}
+
+int
+zfs_getmdnobj(device_t dev, const char *fsfilename,
+ uint64_t *mdnobj)
+{
+ struct zfs_data *data;
+ int err;
+ int isfs;
+
+ data = zfs_mount(dev);
+ if (!data)
+ return ZFS_ERR_BAD_FS;
+
+ err = dnode_get_fullpath(fsfilename, &(data->mdn), mdnobj,
+ &(data->dnode), &isfs, data);
+ zfs_unmount(data);
+ return err;
+}
+
+static void
+fill_fs_info(struct zfs_dirhook_info *info,
+ dnode_end_t mdn, struct zfs_data *data)
+{
+ int err;
+ dnode_end_t dn;
+ uint64_t objnum;
+ uint64_t headobj;
+
+ memset(info, 0, sizeof(*info));
+
+ info->dir = 1;
+
+ if (mdn.dn.dn_type == DMU_OT_DSL_DIR) {
+ headobj = zfs_to_cpu64(((dsl_dir_phys_t *) DN_BONUS(&mdn.dn))->dd_head_dataset_obj, mdn.endian);
+
+ err = dnode_get(&(data->mos), headobj, DMU_OT_DSL_DATASET, &mdn, data);
+ if (err) {
+ printf("zfs failed here 1\n");
+ return;
+ }
+ }
+ make_mdn(&mdn, data);
+ err = dnode_get(&mdn, MASTER_NODE_OBJ, DMU_OT_MASTER_NODE,
+ &dn, data);
+ if (err) {
+ printf("zfs failed here 2\n");
+ return;
+ }
+
+ err = zap_lookup(&dn, ZFS_ROOT_OBJ, &objnum, data);
+ if (err) {
+ printf("zfs failed here 3\n");
+ return;
+ }
+
+ err = dnode_get(&mdn, objnum, 0, &dn, data);
+ if (err) {
+ printf("zfs failed here 4\n");
+ return;
+ }
+
+ info->mtimeset = 1;
+ info->mtime = zfs_to_cpu64(((znode_phys_t *) DN_BONUS(&dn.dn))->zp_mtime[0], dn.endian);
+
+ return;
+}
+
+static int iterate_zap(const char *name, uint64_t val, struct zfs_data *data)
+{
+ struct zfs_dirhook_info info;
+ dnode_end_t dn;
+
+ memset(&info, 0, sizeof(info));
+
+ dnode_get(&(data->mdn), val, 0, &dn, data);
+ info.mtimeset = 1;
+ info.mtime = zfs_to_cpu64(((znode_phys_t *) DN_BONUS(&dn.dn))->zp_mtime[0], dn.endian);
+ info.dir = (dn.dn.dn_type == DMU_OT_DIRECTORY_CONTENTS);
+ debug("zfs type=%d, name=%s\n",
+ (int)dn.dn.dn_type, (char *)name);
+ if (!data->userhook)
+ return 0;
+ return data->userhook(name, &info);
+}
+
+static int iterate_zap_fs(const char *name, uint64_t val, struct zfs_data *data)
+{
+ struct zfs_dirhook_info info;
+ dnode_end_t mdn;
+ int err;
+ err = dnode_get(&(data->mos), val, 0, &mdn, data);
+ if (err)
+ return 0;
+ if (mdn.dn.dn_type != DMU_OT_DSL_DIR)
+ return 0;
+
+ fill_fs_info(&info, mdn, data);
+
+ if (!data->userhook)
+ return 0;
+ return data->userhook(name, &info);
+}
+
+static int iterate_zap_snap(const char *name, uint64_t val, struct zfs_data *data)
+{
+ struct zfs_dirhook_info info;
+ char *name2;
+ int ret = 0;
+ dnode_end_t mdn;
+ int err;
+
+ err = dnode_get(&(data->mos), val, 0, &mdn, data);
+ if (err)
+ return 0;
+
+ if (mdn.dn.dn_type != DMU_OT_DSL_DATASET)
+ return 0;
+
+ fill_fs_info(&info, mdn, data);
+
+ name2 = malloc(strlen(name) + 2);
+ name2[0] = '@';
+ memcpy(name2 + 1, name, strlen(name) + 1);
+ if (data->userhook)
+ ret = data->userhook(name2, &info);
+ free(name2);
+ return ret;
+}
+
+int
+zfs_ls(device_t device, const char *path,
+ int (*hook)(const char *, const struct zfs_dirhook_info *))
+{
+ struct zfs_data *data;
+ int err;
+ int isfs;
+
+ data = zfs_mount(device);
+ if (!data)
+ return ZFS_ERR_BAD_FS;
+
+ data->userhook = hook;
+
+ err = dnode_get_fullpath(path, &(data->mdn), 0, &(data->dnode), &isfs, data);
+ if (err) {
+ zfs_unmount(data);
+ return err;
+ }
+ if (isfs) {
+ uint64_t childobj, headobj;
+ uint64_t snapobj;
+ dnode_end_t dn;
+ struct zfs_dirhook_info info;
+
+ fill_fs_info(&info, data->dnode, data);
+ hook("@", &info);
+
+ childobj = zfs_to_cpu64(((dsl_dir_phys_t *) DN_BONUS(&data->dnode.dn))->dd_child_dir_zapobj, data->dnode.endian);
+ headobj = zfs_to_cpu64(((dsl_dir_phys_t *) DN_BONUS(&data->dnode.dn))->dd_head_dataset_obj, data->dnode.endian);
+ err = dnode_get(&(data->mos), childobj,
+ DMU_OT_DSL_DIR_CHILD_MAP, &dn, data);
+ if (err) {
+ zfs_unmount(data);
+ return err;
+ }
+
+
+ zap_iterate(&dn, iterate_zap_fs, data);
+
+ err = dnode_get(&(data->mos), headobj, DMU_OT_DSL_DATASET, &dn, data);
+ if (err) {
+ zfs_unmount(data);
+ return err;
+ }
+
+ snapobj = zfs_to_cpu64(((dsl_dataset_phys_t *) DN_BONUS(&dn.dn))->ds_snapnames_zapobj, dn.endian);
+
+ err = dnode_get(&(data->mos), snapobj,
+ DMU_OT_DSL_DS_SNAP_MAP, &dn, data);
+ if (err) {
+ zfs_unmount(data);
+ return err;
+ }
+
+ zap_iterate(&dn, iterate_zap_snap, data);
+ } else {
+ if (data->dnode.dn.dn_type != DMU_OT_DIRECTORY_CONTENTS) {
+ zfs_unmount(data);
+ printf("not a directory\n");
+ return ZFS_ERR_BAD_FILE_TYPE;
+ }
+ zap_iterate(&(data->dnode), iterate_zap, data);
+ }
+ zfs_unmount(data);
+ return ZFS_ERR_NONE;
+}
diff --git a/roms/u-boot/fs/zfs/zfs_fletcher.c b/roms/u-boot/fs/zfs/zfs_fletcher.c
new file mode 100644
index 000000000..008a303ec
--- /dev/null
+++ b/roms/u-boot/fs/zfs/zfs_fletcher.c
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc.
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <linux/stat.h>
+#include <linux/time.h>
+#include <linux/ctype.h>
+#include <asm/byteorder.h>
+#include "zfs_common.h"
+
+#include <zfs/zfs.h>
+#include <zfs/zio.h>
+#include <zfs/dnode.h>
+#include <zfs/uberblock_impl.h>
+#include <zfs/vdev_impl.h>
+#include <zfs/zio_checksum.h>
+#include <zfs/zap_impl.h>
+#include <zfs/zap_leaf.h>
+#include <zfs/zfs_znode.h>
+#include <zfs/dmu.h>
+#include <zfs/dmu_objset.h>
+#include <zfs/dsl_dir.h>
+#include <zfs/dsl_dataset.h>
+
+void
+fletcher_2_endian(const void *buf, uint64_t size,
+ zfs_endian_t endian,
+ zio_cksum_t *zcp)
+{
+ const uint64_t *ip = buf;
+ const uint64_t *ipend = ip + (size / sizeof(uint64_t));
+ uint64_t a0, b0, a1, b1;
+
+ for (a0 = b0 = a1 = b1 = 0; ip < ipend; ip += 2) {
+ a0 += zfs_to_cpu64(ip[0], endian);
+ a1 += zfs_to_cpu64(ip[1], endian);
+ b0 += a0;
+ b1 += a1;
+ }
+
+ zcp->zc_word[0] = cpu_to_zfs64(a0, endian);
+ zcp->zc_word[1] = cpu_to_zfs64(a1, endian);
+ zcp->zc_word[2] = cpu_to_zfs64(b0, endian);
+ zcp->zc_word[3] = cpu_to_zfs64(b1, endian);
+}
+
+void
+fletcher_4_endian(const void *buf, uint64_t size, zfs_endian_t endian,
+ zio_cksum_t *zcp)
+{
+ const uint32_t *ip = buf;
+ const uint32_t *ipend = ip + (size / sizeof(uint32_t));
+ uint64_t a, b, c, d;
+
+ for (a = b = c = d = 0; ip < ipend; ip++) {
+ a += zfs_to_cpu32(ip[0], endian);
+ b += a;
+ c += b;
+ d += c;
+ }
+
+ zcp->zc_word[0] = cpu_to_zfs64(a, endian);
+ zcp->zc_word[1] = cpu_to_zfs64(b, endian);
+ zcp->zc_word[2] = cpu_to_zfs64(c, endian);
+ zcp->zc_word[3] = cpu_to_zfs64(d, endian);
+}
diff --git a/roms/u-boot/fs/zfs/zfs_lzjb.c b/roms/u-boot/fs/zfs/zfs_lzjb.c
new file mode 100644
index 000000000..b42d49801
--- /dev/null
+++ b/roms/u-boot/fs/zfs/zfs_lzjb.c
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc.
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <linux/stat.h>
+#include <linux/time.h>
+#include <linux/ctype.h>
+#include <asm/byteorder.h>
+#include "zfs_common.h"
+
+#include <zfs/zfs.h>
+#include <zfs/zio.h>
+#include <zfs/dnode.h>
+#include <zfs/uberblock_impl.h>
+#include <zfs/vdev_impl.h>
+#include <zfs/zio_checksum.h>
+#include <zfs/zap_impl.h>
+#include <zfs/zap_leaf.h>
+#include <zfs/zfs_znode.h>
+#include <zfs/dmu.h>
+#include <zfs/dmu_objset.h>
+#include <zfs/dsl_dir.h>
+#include <zfs/dsl_dataset.h>
+
+#define MATCH_BITS 6
+#define MATCH_MIN 3
+#define OFFSET_MASK ((1 << (16 - MATCH_BITS)) - 1)
+
+/*
+ * Decompression Entry - lzjb
+ */
+#ifndef NBBY
+#define NBBY 8
+#endif
+
+int
+lzjb_decompress(void *s_start, void *d_start, uint32_t s_len,
+ uint32_t d_len)
+{
+ uint8_t *src = s_start;
+ uint8_t *dst = d_start;
+ uint8_t *d_end = (uint8_t *) d_start + d_len;
+ uint8_t *s_end = (uint8_t *) s_start + s_len;
+ uint8_t *cpy, copymap = 0;
+ int copymask = 1 << (NBBY - 1);
+
+ while (dst < d_end && src < s_end) {
+ if ((copymask <<= 1) == (1 << NBBY)) {
+ copymask = 1;
+ copymap = *src++;
+ }
+ if (src >= s_end) {
+ printf("lzjb decompression failed\n");
+ return ZFS_ERR_BAD_FS;
+ }
+ if (copymap & copymask) {
+ int mlen = (src[0] >> (NBBY - MATCH_BITS)) + MATCH_MIN;
+ int offset = ((src[0] << NBBY) | src[1]) & OFFSET_MASK;
+ src += 2;
+ cpy = dst - offset;
+ if (src > s_end || cpy < (uint8_t *) d_start) {
+ printf("lzjb decompression failed\n");
+ return ZFS_ERR_BAD_FS;
+ }
+ while (--mlen >= 0 && dst < d_end)
+ *dst++ = *cpy++;
+ } else {
+ *dst++ = *src++;
+ }
+ }
+ if (dst < d_end) {
+ printf("lzjb decompression failed\n");
+ return ZFS_ERR_BAD_FS;
+ }
+ return ZFS_ERR_NONE;
+}
diff --git a/roms/u-boot/fs/zfs/zfs_sha256.c b/roms/u-boot/fs/zfs/zfs_sha256.c
new file mode 100644
index 000000000..cb5b1c068
--- /dev/null
+++ b/roms/u-boot/fs/zfs/zfs_sha256.c
@@ -0,0 +1,135 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc.
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <linux/stat.h>
+#include <linux/time.h>
+#include <linux/ctype.h>
+#include <asm/byteorder.h>
+#include "zfs_common.h"
+
+#include <zfs/zfs.h>
+#include <zfs/zio.h>
+#include <zfs/dnode.h>
+#include <zfs/uberblock_impl.h>
+#include <zfs/vdev_impl.h>
+#include <zfs/zio_checksum.h>
+#include <zfs/zap_impl.h>
+#include <zfs/zap_leaf.h>
+#include <zfs/zfs_znode.h>
+#include <zfs/dmu.h>
+#include <zfs/dmu_objset.h>
+#include <zfs/dsl_dir.h>
+#include <zfs/dsl_dataset.h>
+
+/*
+ * SHA-256 checksum, as specified in FIPS 180-2, available at:
+ * http://csrc.nist.gov/cryptval
+ *
+ * This is a very compact implementation of SHA-256.
+ * It is designed to be simple and portable, not to be fast.
+ */
+
+/*
+ * The literal definitions according to FIPS180-2 would be:
+ *
+ * Ch(x, y, z) (((x) & (y)) ^ ((~(x)) & (z)))
+ * Maj(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z)))
+ *
+ * We use logical equivalents which require one less op.
+ */
+#define Ch(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
+#define Maj(x, y, z) (((x) & (y)) ^ ((z) & ((x) ^ (y))))
+#define Rot32(x, s) (((x) >> s) | ((x) << (32 - s)))
+#define SIGMA0(x) (Rot32(x, 2) ^ Rot32(x, 13) ^ Rot32(x, 22))
+#define SIGMA1(x) (Rot32(x, 6) ^ Rot32(x, 11) ^ Rot32(x, 25))
+#define sigma0(x) (Rot32(x, 7) ^ Rot32(x, 18) ^ ((x) >> 3))
+#define sigma1(x) (Rot32(x, 17) ^ Rot32(x, 19) ^ ((x) >> 10))
+
+static const uint32_t SHA256_K[64] = {
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+ 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+ 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+ 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+ 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+ 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+ 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+ 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+};
+
+static void
+SHA256Transform(uint32_t *H, const uint8_t *cp)
+{
+ uint32_t a, b, c, d, e, f, g, h, t, T1, T2, W[64];
+
+ for (t = 0; t < 16; t++, cp += 4)
+ W[t] = (cp[0] << 24) | (cp[1] << 16) | (cp[2] << 8) | cp[3];
+
+ for (t = 16; t < 64; t++)
+ W[t] = sigma1(W[t - 2]) + W[t - 7] +
+ sigma0(W[t - 15]) + W[t - 16];
+
+ a = H[0]; b = H[1]; c = H[2]; d = H[3];
+ e = H[4]; f = H[5]; g = H[6]; h = H[7];
+
+ for (t = 0; t < 64; t++) {
+ T1 = h + SIGMA1(e) + Ch(e, f, g) + SHA256_K[t] + W[t];
+ T2 = SIGMA0(a) + Maj(a, b, c);
+ h = g; g = f; f = e; e = d + T1;
+ d = c; c = b; b = a; a = T1 + T2;
+ }
+
+ H[0] += a; H[1] += b; H[2] += c; H[3] += d;
+ H[4] += e; H[5] += f; H[6] += g; H[7] += h;
+}
+
+void
+zio_checksum_SHA256(const void *buf, uint64_t size,
+ zfs_endian_t endian, zio_cksum_t *zcp)
+{
+ uint32_t H[8] = { 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
+ 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 };
+ uint8_t pad[128];
+ unsigned padsize = size & 63;
+ unsigned i;
+
+ for (i = 0; i < size - padsize; i += 64)
+ SHA256Transform(H, (uint8_t *)buf + i);
+
+ for (i = 0; i < padsize; i++)
+ pad[i] = ((uint8_t *)buf)[i];
+
+ for (pad[padsize++] = 0x80; (padsize & 63) != 56; padsize++)
+ pad[padsize] = 0;
+
+ for (i = 0; i < 8; i++)
+ pad[padsize++] = (size << 3) >> (56 - 8 * i);
+
+ for (i = 0; i < padsize; i += 64)
+ SHA256Transform(H, pad + i);
+
+ zcp->zc_word[0] = cpu_to_zfs64((uint64_t)H[0] << 32 | H[1],
+ endian);
+ zcp->zc_word[1] = cpu_to_zfs64((uint64_t)H[2] << 32 | H[3],
+ endian);
+ zcp->zc_word[2] = cpu_to_zfs64((uint64_t)H[4] << 32 | H[5],
+ endian);
+ zcp->zc_word[3] = cpu_to_zfs64((uint64_t)H[6] << 32 | H[7],
+ endian);
+}