// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2024, Huawei Technologies Co, Ltd.
 *
 * Authors: Zhihao Cheng <chengzhihao1@huawei.com>
 */

#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <sys/stat.h>

#include <linux/limits.h>

#include "linux_err.h"
#include "bitops.h"
#include "kmem.h"
#include "crc32.h"
#include "ubifs.h"
#include "defs.h"
#include "debug.h"
#include "key.h"
#include "misc.h"
#include "fsck.ubifs.h"

static void parse_node_header(int lnum, int offs, int len,
			      unsigned long long sqnum,
			      struct scanned_node *header)
{
	header->exist = true;
	header->lnum = lnum;
	header->offs = offs;
	header->len = len;
	header->sqnum = sqnum;
}

static inline bool inode_can_be_encrypted(struct ubifs_info *c,
					  struct scanned_ino_node *ino_node)
{
	if (!c->encrypted)
		return false;

	if (ino_node->is_xattr)
		return false;

	/* Only regular files, directories, and symlinks can be encrypted. */
	if (S_ISREG(ino_node->mode) || S_ISDIR(ino_node->mode) ||
	    S_ISLNK(ino_node->mode))
		return true;

	return false;
}

/**
 * parse_ino_node - parse inode node and check it's validity.
 * @c: UBIFS file-system description object
 * @lnum: logical eraseblock number
 * @offs: the offset in LEB of the raw inode node
 * @node: raw node
 * @key: key of node scanned (if it has one)
 * @ino_node: node used to store raw inode information
 *
 * This function checks the raw inode information, and stores inode
 * information into @ino_node. Returns %true if the inode is valid,
 * otherwise %false is returned.
 */
bool parse_ino_node(struct ubifs_info *c, int lnum, int offs, void *node,
		    union ubifs_key *key, struct scanned_ino_node *ino_node)
{
	bool valid = false;
	int data_len, node_len;
	unsigned int flags;
	unsigned long long sqnum;
	struct ubifs_ch *ch = (struct ubifs_ch *)node;
	struct ubifs_ino_node *ino = (struct ubifs_ino_node *)node;
	ino_t inum = key_inum(c, key);

	if (!inum || inum > INUM_WATERMARK) {
		if (FSCK(c)->mode == REBUILD_MODE)
			dbg_fsck("bad inode node(bad inum %lu) at %d:%d, in %s",
				 inum, lnum, offs, c->dev_name);
		else
			log_out(c, "bad inode node(bad inum %lu) at %d:%d",
				inum, lnum, offs);
		goto out;
	}

	if (ch->node_type != key_type(c, key)) {
		if (FSCK(c)->mode == REBUILD_MODE)
			dbg_fsck("bad inode node %lu(inconsistent node type %d vs key_type %d) at %d:%d, in %s",
				 inum, ch->node_type, key_type(c, key),
				 lnum, offs, c->dev_name);
		else
			log_out(c, "bad inode node %lu(inconsistent node type %d vs key_type %d) at %d:%d",
				inum, ch->node_type, key_type(c, key),
				lnum, offs);
		goto out;
	}

	node_len = le32_to_cpu(ch->len);
	sqnum = le64_to_cpu(ch->sqnum);
	key_copy(c, key, &ino_node->key);
	flags = le32_to_cpu(ino->flags);
	data_len = le32_to_cpu(ino->data_len);
	ino_node->is_xattr = !!(flags & UBIFS_XATTR_FL) ? 1 : 0;
	ino_node->is_encrypted = !!(flags & UBIFS_CRYPT_FL) ? 1 : 0;
	ino_node->mode = le32_to_cpu(ino->mode);
	ino_node->nlink = le32_to_cpu(ino->nlink);
	ino_node->xcnt = le32_to_cpu(ino->xattr_cnt);
	ino_node->xsz = le32_to_cpu(ino->xattr_size);
	ino_node->xnms = le32_to_cpu(ino->xattr_names);
	ino_node->size = le64_to_cpu(ino->size);

	if (inum == UBIFS_ROOT_INO && !S_ISDIR(ino_node->mode)) {
		if (FSCK(c)->mode == REBUILD_MODE)
			dbg_fsck("bad inode node %lu(root inode is not dir, tyoe %u) at %d:%d, in %s",
				 inum, ino_node->mode & S_IFMT, lnum, offs,
				 c->dev_name);
		else
			log_out(c, "bad inode node %lu(root inode is not dir, tyoe %u) at %d:%d",
				inum, ino_node->mode & S_IFMT, lnum, offs);
		goto out;
	}

	if (ino_node->size > c->max_inode_sz) {
		if (FSCK(c)->mode == REBUILD_MODE)
			dbg_fsck("bad inode node %lu(size %llu is too large) at %d:%d, in %s",
				 inum, ino_node->size, lnum, offs, c->dev_name);
		else
			log_out(c, "bad inode node %lu(size %llu is too large) at %d:%d",
				inum, ino_node->size, lnum, offs);
		goto out;
	}

	if (le16_to_cpu(ino->compr_type) >= UBIFS_COMPR_TYPES_CNT) {
		if (FSCK(c)->mode == REBUILD_MODE)
			dbg_fsck("bad inode node %lu(unknown compression type %d) at %d:%d, in %s",
				 inum, le16_to_cpu(ino->compr_type), lnum, offs,
				 c->dev_name);
		else
			log_out(c, "bad inode node %lu(unknown compression type %d) at %d:%d",
				inum, le16_to_cpu(ino->compr_type), lnum, offs);
		goto out;
	}

	if (ino_node->xnms + ino_node->xcnt > XATTR_LIST_MAX) {
		if (FSCK(c)->mode == REBUILD_MODE)
			dbg_fsck("bad inode node %lu(too big xnames %u xcount %u) at %d:%d, in %s",
				 inum, ino_node->xnms, ino_node->xcnt,
				 lnum, offs, c->dev_name);
		else
			log_out(c, "bad inode node %lu(too big xnames %u xcount %u) at %d:%d",
				inum, ino_node->xnms, ino_node->xcnt,
				lnum, offs);
		goto out;
	}

	if (data_len < 0 || data_len > UBIFS_MAX_INO_DATA) {
		if (FSCK(c)->mode == REBUILD_MODE)
			dbg_fsck("bad inode node %lu(invalid data len %d) at %d:%d, in %s",
				 inum, data_len, lnum, offs, c->dev_name);
		else
			log_out(c, "bad inode node %lu(invalid data len %d) at %d:%d",
				inum, data_len, lnum, offs);
		goto out;
	}

	if (UBIFS_INO_NODE_SZ + data_len != node_len) {
		if (FSCK(c)->mode == REBUILD_MODE)
			dbg_fsck("bad inode node %lu(inconsistent data len %d vs node len %d) at %d:%d, in %s",
				 inum, data_len, node_len, lnum, offs, c->dev_name);
		else
			log_out(c, "bad inode node %lu(inconsistent data len %d vs node len %d) at %d:%d",
				inum, data_len, node_len, lnum, offs);
		goto out;
	}

	if (ino_node->is_xattr) {
		if (!S_ISREG(ino_node->mode)) {
			if (FSCK(c)->mode == REBUILD_MODE)
				dbg_fsck("bad inode node %lu(bad type %u for xattr) at %d:%d, in %s",
					 inum, ino_node->mode & S_IFMT,
					 lnum, offs, c->dev_name);
			else
				log_out(c, "bad inode node %lu(bad type %u for xattr) at %d:%d",
					inum, ino_node->mode & S_IFMT,
					lnum, offs);
			goto out;
		}
		if (data_len != ino_node->size) {
			if (FSCK(c)->mode == REBUILD_MODE)
				dbg_fsck("bad inode node %lu(inconsistent data_len %d vs size %llu for xattr) at %d:%d, in %s",
					 inum, data_len, ino_node->size,
					 lnum, offs, c->dev_name);
			else
				log_out(c, "bad inode node %lu(inconsistent data_len %d vs size %llu for xattr) at %d:%d",
					inum, data_len, ino_node->size,
					lnum, offs);
			goto out;
		}
		if (ino_node->xcnt || ino_node->xsz || ino_node->xnms) {
			if (FSCK(c)->mode == REBUILD_MODE)
				dbg_fsck("bad inode node %lu(non zero xattr count %u xattr size %u xattr names %u for xattr) at %d:%d, in %s",
					 inum, ino_node->xcnt, ino_node->xsz,
					 ino_node->xnms, lnum, offs, c->dev_name);
			else
				log_out(c, "bad inode node %lu(non zero xattr count %u xattr size %u xattr names %u for xattr) at %d:%d",
					inum, ino_node->xcnt, ino_node->xsz,
					ino_node->xnms, lnum, offs);
			goto out;
		}
	}

	switch (ino_node->mode & S_IFMT) {
	case S_IFREG:
		if (!ino_node->is_xattr && data_len != 0) {
			if (FSCK(c)->mode == REBUILD_MODE)
				dbg_fsck("bad inode node %lu(bad data len %d for reg file) at %d:%d, in %s",
					 inum, data_len, lnum, offs, c->dev_name);
			else
				log_out(c, "bad inode node %lu(bad data len %d for reg file) at %d:%d",
					inum, data_len, lnum, offs);
			goto out;
		}
		break;
	case S_IFDIR:
		if (data_len != 0) {
			if (FSCK(c)->mode == REBUILD_MODE)
				dbg_fsck("bad inode node %lu(bad data len %d for dir file) at %d:%d, in %s",
					 inum, data_len, lnum, offs, c->dev_name);
			else
				log_out(c, "bad inode node %lu(bad data len %d for dir file) at %d:%d",
					inum, data_len, lnum, offs);
			goto out;
		}
		break;
	case S_IFLNK:
		if (data_len == 0) {
			/*
			 * For encryption enabled or selinux enabled situation,
			 * uninitialized inode with xattrs could be written
			 * before ubifs_jnl_update(). If the dent node is
			 * written successfully but the initialized inode is
			 * not written, ubifs_iget() will get bad symlink inode
			 * with 'ui->data_len = 0'. Similar phenomenon can also
			 * occur for block/char dev creation.
			 * Just drop the inode node when above class of
			 * exceptions are found.
			 */
			if (FSCK(c)->mode == REBUILD_MODE)
				dbg_fsck("bad symlink inode node %lu(bad data len %d) at %d:%d, in %s",
					 inum, data_len, lnum, offs, c->dev_name);
			else
				log_out(c, "bad symlink inode node %lu(bad data len %d) at %d:%d",
					inum, data_len, lnum, offs);
			goto out;
		}
		break;
	case S_IFBLK:
		fallthrough;
	case S_IFCHR:
	{
		union ubifs_dev_desc *dev = (union ubifs_dev_desc *)ino->data;
		int sz_new = sizeof(dev->new), sz_huge = sizeof(dev->huge);

		if (data_len != sz_new && data_len != sz_huge) {
			if (FSCK(c)->mode == REBUILD_MODE)
				dbg_fsck("bad inode node %lu(bad data len %d for char/block file, expect %d or %d) at %d:%d, in %s",
					 inum, data_len, sz_new, sz_huge, lnum,
					 offs, c->dev_name);
			else
				log_out(c, "bad inode node %lu(bad data len %d for char/block file, expect %d or %d) at %d:%d",
					inum, data_len, sz_new, sz_huge, lnum,
					offs);
			goto out;
		}
		break;
	}
	case S_IFSOCK:
		fallthrough;
	case S_IFIFO:
		if (data_len != 0) {
			if (FSCK(c)->mode == REBUILD_MODE)
				dbg_fsck("bad inode node %lu(bad data len %d for fifo/sock file) at %d:%d, in %s",
					 inum, data_len, lnum, offs, c->dev_name);
			else
				log_out(c, "bad inode node %lu(bad data len %d for fifo/sock file) at %d:%d",
					inum, data_len, lnum, offs);
			goto out;
		}
		break;
	default:
		/* invalid file type. */
		if (FSCK(c)->mode == REBUILD_MODE)
			dbg_fsck("bad inode node %lu(unknown type %u) at %d:%d, in %s",
				 inum, ino_node->mode & S_IFMT, lnum, offs, c->dev_name);
		else
			log_out(c, "bad inode node %lu(unknown type %u) at %d:%d",
				inum, ino_node->mode & S_IFMT, lnum, offs);
		goto out;
	}

	if (ino_node->is_encrypted && !inode_can_be_encrypted(c, ino_node)) {
		if (FSCK(c)->mode == REBUILD_MODE)
			dbg_fsck("bad inode node %lu(encrypted but cannot be encrypted, type %u, is_xattr %d, fs_encrypted %d) at %d:%d, in %s",
				 inum, ino_node->mode & S_IFMT, ino_node->is_xattr,
				 c->encrypted, lnum, offs, c->dev_name);
		else
			log_out(c, "bad inode node %lu(encrypted but cannot be encrypted, type %u, is_xattr %d, fs_encrypted %d) at %d:%d",
				inum, ino_node->mode & S_IFMT, ino_node->is_xattr,
				c->encrypted, lnum, offs);
		goto out;
	}

	valid = true;
	parse_node_header(lnum, offs, node_len, sqnum, &ino_node->header);

out:
	return valid;
}

/**
 * parse_dent_node - parse dentry node and check it's validity.
 * @c: UBIFS file-system description object
 * @lnum: logical eraseblock number
 * @offs: the offset in LEB of the raw inode node
 * @node: raw node
 * @key: key of node scanned (if it has one)
 * @dent_node: node used to store raw dentry information
 *
 * This function checks the raw dentry/(xattr entry) information, and
 * stores dentry/(xattr entry) information into @dent_node. Returns
 * %true if the entry is valid, otherwise %false is returned.
 */
bool parse_dent_node(struct ubifs_info *c, int lnum, int offs, void *node,
		     union ubifs_key *key, struct scanned_dent_node *dent_node)
{
	bool valid = false;
	int node_len, nlen;
	unsigned long long sqnum;
	struct ubifs_ch *ch = (struct ubifs_ch *)node;
	struct ubifs_dent_node *dent = (struct ubifs_dent_node *)node;
	int key_type = key_type_flash(c, dent->key);
	ino_t inum;

	nlen = le16_to_cpu(dent->nlen);
	node_len = le32_to_cpu(ch->len);
	sqnum = le64_to_cpu(ch->sqnum);
	inum = le64_to_cpu(dent->inum);

	if (node_len != nlen + UBIFS_DENT_NODE_SZ + 1 ||
	    dent->type >= UBIFS_ITYPES_CNT ||
	    nlen > UBIFS_MAX_NLEN || dent->name[nlen] != 0 ||
	    (key_type == UBIFS_XENT_KEY &&
	     strnlen((const char *)dent->name, nlen) != nlen) ||
	    inum > INUM_WATERMARK || key_type != ch->node_type) {
		if (FSCK(c)->mode == REBUILD_MODE)
			dbg_fsck("bad %s node(len %d nlen %d type %d inum %lu key_type %d node_type %d) at %d:%d, in %s",
				 ch->node_type == UBIFS_XENT_NODE ? "xattr entry" : "directory entry",
				 node_len, nlen, dent->type, inum, key_type,
				 ch->node_type, lnum, offs, c->dev_name);
		else
			log_out(c, "bad %s node(len %d nlen %d type %d inum %lu key_type %d node_type %d) at %d:%d",
				ch->node_type == UBIFS_XENT_NODE ? "xattr entry" : "directory entry",
				node_len, nlen, dent->type, inum, key_type,
				ch->node_type, lnum, offs);
		goto out;
	}

	key_copy(c, key, &dent_node->key);
	dent_node->can_be_found = false;
	dent_node->type = dent->type;
	dent_node->nlen = nlen;
	memcpy(dent_node->name, dent->name, nlen);
	dent_node->name[nlen] = '\0';
	dent_node->inum = inum;

	valid = true;
	parse_node_header(lnum, offs, node_len, sqnum, &dent_node->header);

out:
	return valid;
}

/**
 * parse_data_node - parse data node and check it's validity.
 * @c: UBIFS file-system description object
 * @lnum: logical eraseblock number
 * @offs: the offset in LEB of the raw data node
 * @node: raw node
 * @key: key of node scanned (if it has one)
 * @ino_node: node used to store raw data information
 *
 * This function checks the raw data node information, and stores
 * data node information into @data_node. Returns %true if the data
 * node is valid, otherwise %false is returned.
 */
bool parse_data_node(struct ubifs_info *c, int lnum, int offs, void *node,
		     union ubifs_key *key, struct scanned_data_node *data_node)
{
	bool valid = false;
	int node_len;
	unsigned long long sqnum;
	struct ubifs_ch *ch = (struct ubifs_ch *)node;
	struct ubifs_data_node *dn = (struct ubifs_data_node *)node;
	ino_t inum = key_inum(c, key);

	if (ch->node_type != key_type(c, key)) {
		if (FSCK(c)->mode == REBUILD_MODE)
			dbg_fsck("bad data node(inconsistent node type %d vs key_type %d) at %d:%d, in %s",
				 ch->node_type, key_type(c, key),
				 lnum, offs, c->dev_name);
		else
			log_out(c, "bad data node(inconsistent node type %d vs key_type %d) at %d:%d",
				ch->node_type, key_type(c, key), lnum, offs);
		goto out;
	}

	if (!inum || inum > INUM_WATERMARK) {
		if (FSCK(c)->mode == REBUILD_MODE)
			dbg_fsck("bad data node(bad inum %lu) at %d:%d, in %s",
				 inum, lnum, offs, c->dev_name);
		else
			log_out(c, "bad data node(bad inum %lu) at %d:%d",
				inum, lnum, offs);
		goto out;
	}

	node_len = le32_to_cpu(ch->len);
	sqnum = le64_to_cpu(ch->sqnum);
	key_copy(c, key, &data_node->key);
	data_node->size = le32_to_cpu(dn->size);

	if (!data_node->size || data_node->size > UBIFS_BLOCK_SIZE) {
		if (FSCK(c)->mode == REBUILD_MODE)
			dbg_fsck("bad data node(invalid size %u) at %d:%d, in %s",
				 data_node->size, lnum, offs, c->dev_name);
		else
			log_out(c, "bad data node(invalid size %u) at %d:%d",
				data_node->size, lnum, offs);
		goto out;
	}

	if (le16_to_cpu(dn->compr_type) >= UBIFS_COMPR_TYPES_CNT) {
		if (FSCK(c)->mode == REBUILD_MODE)
			dbg_fsck("bad data node(invalid compression type %d) at %d:%d, in %s",
				 le16_to_cpu(dn->compr_type), lnum, offs, c->dev_name);
		else
			log_out(c, "bad data node(invalid compression type %d) at %d:%d",
				le16_to_cpu(dn->compr_type), lnum, offs);
		goto out;
	}

	valid = true;
	parse_node_header(lnum, offs, node_len, sqnum, &data_node->header);

out:
	return valid;
}

/**
 * parse_trun_node - parse truncation node and check it's validity.
 * @c: UBIFS file-system description object
 * @lnum: logical eraseblock number
 * @offs: the offset in LEB of the raw truncation node
 * @node: raw node
 * @key: key of node scanned (if it has one)
 * @trun_node: node used to store raw truncation information
 *
 * This function checks the raw truncation information, and stores
 * truncation information into @trun_node. Returns %true if the
 * truncation is valid, otherwise %false is returned.
 */
bool parse_trun_node(struct ubifs_info *c, int lnum, int offs, void *node,
		     union ubifs_key *key, struct scanned_trun_node *trun_node)
{
	bool valid = false;
	int node_len;
	unsigned long long sqnum;
	struct ubifs_ch *ch = (struct ubifs_ch *)node;
	struct ubifs_trun_node *trun = (struct ubifs_trun_node *)node;
	loff_t old_size = le64_to_cpu(trun->old_size);
	loff_t new_size = le64_to_cpu(trun->new_size);
	ino_t inum = le32_to_cpu(trun->inum);

	if (!inum || inum > INUM_WATERMARK) {
		dbg_fsck("bad truncation node(bad inum %lu) at %d:%d, in %s",
			 inum, lnum, offs, c->dev_name);
		goto out;
	}

	node_len = le32_to_cpu(ch->len);
	sqnum = le64_to_cpu(ch->sqnum);
	trun_node->new_size = new_size;

	if (old_size < 0 || old_size > c->max_inode_sz ||
	    new_size < 0 || new_size > c->max_inode_sz ||
	    old_size <= new_size) {
		dbg_fsck("bad truncation node(new size %ld old size %ld inum %lu) at %d:%d, in %s",
			 new_size, old_size, inum, lnum, offs, c->dev_name);
		goto out;
	}

	trun_key_init(c, key, inum);
	valid = true;
	parse_node_header(lnum, offs, node_len, sqnum, &trun_node->header);

out:
	return valid;
}

/**
 * insert_file_dentry - insert dentry according to scanned dent node.
 * @file: file object
 * @n_dent: scanned dent node
 *
 * Insert file dentry information. Returns zero in case of success, a
 * negative error code in case of failure.
 */
static int insert_file_dentry(struct scanned_file *file,
			      struct scanned_dent_node *n_dent)
{
	struct scanned_dent_node *dent;
	struct rb_node **p, *parent = NULL;

	p = &file->dent_nodes.rb_node;
	while (*p) {
		parent = *p;
		dent = rb_entry(parent, struct scanned_dent_node, rb);
		if (n_dent->header.sqnum < dent->header.sqnum)
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}

	dent = kmalloc(sizeof(struct scanned_dent_node), GFP_KERNEL);
	if (!dent)
		return -ENOMEM;

	*dent = *n_dent;
	rb_link_node(&dent->rb, parent, p);
	rb_insert_color(&dent->rb, &file->dent_nodes);

	return 0;
}

/**
 * update_file_data - insert/update data according to scanned data node.
 * @c: UBIFS file-system description object
 * @file: file object
 * @n_dn: scanned data node
 *
 * Insert or update file data information. Returns zero in case of success,
 * a negative error code in case of failure.
 */
static int update_file_data(struct ubifs_info *c, struct scanned_file *file,
			    struct scanned_data_node *n_dn)
{
	int cmp;
	struct scanned_data_node *dn, *o_dn = NULL;
	struct rb_node **p, *parent = NULL;

	p = &file->data_nodes.rb_node;
	while (*p) {
		parent = *p;
		dn = rb_entry(parent, struct scanned_data_node, rb);
		cmp = keys_cmp(c, &n_dn->key, &dn->key);
		if (cmp < 0) {
			p = &(*p)->rb_left;
		} else if (cmp > 0) {
			p = &(*p)->rb_right;
		} else {
			o_dn = dn;
			break;
		}
	}

	if (o_dn) {
		/* found data node with same block no. */
		if (o_dn->header.sqnum < n_dn->header.sqnum) {
			o_dn->header = n_dn->header;
			o_dn->size = n_dn->size;
		}

		return 0;
	}

	dn = kmalloc(sizeof(struct scanned_data_node), GFP_KERNEL);
	if (!dn)
		return -ENOMEM;

	*dn = *n_dn;
	INIT_LIST_HEAD(&dn->list);
	rb_link_node(&dn->rb, parent, p);
	rb_insert_color(&dn->rb, &file->data_nodes);

	return 0;
}

/**
 * update_file - update file information.
 * @c: UBIFS file-system description object
 * @file: file object
 * @sn: scanned node
 * @key_type: type of @sn
 *
 * Update inode/dent/truncation/data node information of @file. Returns
 * zero in case of success, a negative error code in case of failure.
 */
static int update_file(struct ubifs_info *c, struct scanned_file *file,
		       struct scanned_node *sn, int key_type)
{
	int err = 0;

	switch (key_type) {
	case UBIFS_INO_KEY:
	{
		struct scanned_ino_node *o_ino, *n_ino;

		o_ino = &file->ino;
		n_ino = (struct scanned_ino_node *)sn;
		if (o_ino->header.exist && o_ino->header.sqnum > sn->sqnum)
			goto out;

		*o_ino = *n_ino;
		break;
	}
	case UBIFS_DENT_KEY:
	case UBIFS_XENT_KEY:
	{
		struct scanned_dent_node *dent = (struct scanned_dent_node *)sn;

		dent->file = file;
		err = insert_file_dentry(file, dent);
		break;
	}
	case UBIFS_DATA_KEY:
	{
		struct scanned_data_node *dn = (struct scanned_data_node *)sn;

		err = update_file_data(c, file, dn);
		break;
	}
	case UBIFS_TRUN_KEY:
	{
		struct scanned_trun_node *o_trun, *n_trun;

		o_trun = &file->trun;
		n_trun = (struct scanned_trun_node *)sn;
		if (o_trun->header.exist && o_trun->header.sqnum > sn->sqnum)
			goto out;

		*o_trun = *n_trun;
		break;
	}
	default:
		err = -EINVAL;
		log_err(c, 0, "unknown key type %d", key_type);
	}

out:
	return err;
}

/**
 * insert_or_update_file - insert or update file according to scanned node.
 * @c: UBIFS file-system description object
 * @file_tree: tree of all scanned files
 * @sn: scanned node
 * @key_type: key type of @sn
 * @inum: inode number
 *
 * According to @sn, this function inserts file into the tree, or updates
 * file information if it already exists in the tree. Returns zero in case
 * of success, a negative error code in case of failure.
 */
int insert_or_update_file(struct ubifs_info *c, struct rb_root *file_tree,
			  struct scanned_node *sn, int key_type, ino_t inum)
{
	int err;
	struct scanned_file *file, *old_file = NULL;
	struct rb_node **p, *parent = NULL;

	p = &file_tree->rb_node;
	while (*p) {
		parent = *p;
		file = rb_entry(parent, struct scanned_file, rb);
		if (inum < file->inum) {
			p = &(*p)->rb_left;
		} else if (inum > file->inum) {
			p = &(*p)->rb_right;
		} else {
			old_file = file;
			break;
		}
	}
	if (old_file)
		return update_file(c, old_file, sn, key_type);

	file = kzalloc(sizeof(struct scanned_file), GFP_KERNEL);
	if (!file)
		return -ENOMEM;

	file->inum = inum;
	file->dent_nodes = RB_ROOT;
	file->data_nodes = RB_ROOT;
	file->xattr_files = RB_ROOT;
	INIT_LIST_HEAD(&file->list);
	err = update_file(c, file, sn, key_type);
	if (err) {
		kfree(file);
		return err;
	}
	rb_link_node(&file->rb, parent, p);
	rb_insert_color(&file->rb, file_tree);

	return 0;
}

/**
 * destroy_file_content - destroy scanned data/dentry nodes in give file.
 * @c: UBIFS file-system description object
 * @file: file object
 *
 * Destroy all data/dentry nodes and xattrs attached to @file.
 */
void destroy_file_content(struct ubifs_info *c, struct scanned_file *file)
{
	struct scanned_data_node *data_node;
	struct scanned_dent_node *dent_node;
	struct scanned_file *xattr_file;
	struct rb_node *this;

	this = rb_first(&file->data_nodes);
	while (this) {
		data_node = rb_entry(this, struct scanned_data_node, rb);
		this = rb_next(this);

		rb_erase(&data_node->rb, &file->data_nodes);
		kfree(data_node);
	}

	this = rb_first(&file->dent_nodes);
	while (this) {
		dent_node = rb_entry(this, struct scanned_dent_node, rb);
		this = rb_next(this);

		rb_erase(&dent_node->rb, &file->dent_nodes);
		kfree(dent_node);
	}

	this = rb_first(&file->xattr_files);
	while (this) {
		xattr_file = rb_entry(this, struct scanned_file, rb);
		this = rb_next(this);

		ubifs_assert(c, !rb_first(&xattr_file->xattr_files));
		destroy_file_content(c, xattr_file);
		rb_erase(&xattr_file->rb, &file->xattr_files);
		kfree(xattr_file);
	}
}

/**
 * destroy_file_tree - destroy files from a given tree.
 * @c: UBIFS file-system description object
 * @file_tree: tree of all scanned files
 *
 * Destroy scanned files from a given tree.
 */
void destroy_file_tree(struct ubifs_info *c, struct rb_root *file_tree)
{
	struct scanned_file *file;
	struct rb_node *this;

	this = rb_first(file_tree);
	while (this) {
		file = rb_entry(this, struct scanned_file, rb);
		this = rb_next(this);

		destroy_file_content(c, file);

		rb_erase(&file->rb, file_tree);
		kfree(file);
	}
}

/**
 * destroy_file_list - destroy files from a given list head.
 * @c: UBIFS file-system description object
 * @file_list: list of the scanned files
 *
 * Destroy scanned files from a given list.
 */
void destroy_file_list(struct ubifs_info *c, struct list_head *file_list)
{
	struct scanned_file *file;

	while (!list_empty(file_list)) {
		file = list_entry(file_list->next, struct scanned_file, list);

		destroy_file_content(c, file);
		list_del(&file->list);
		kfree(file);
	}
}

/**
 * lookup_file - lookup file according to inode number.
 * @file_tree: tree of all scanned files
 * @inum: inode number
 *
 * This function lookups target file from @file_tree according to @inum.
 */
struct scanned_file *lookup_file(struct rb_root *file_tree, ino_t inum)
{
	struct scanned_file *file;
	struct rb_node *p;

	p = file_tree->rb_node;
	while (p) {
		file = rb_entry(p, struct scanned_file, rb);

		if (inum < file->inum)
			p = p->rb_left;
		else if (inum > file->inum)
			p = p->rb_right;
		else
			return file;
	}

	return NULL;
}

static void handle_invalid_file(struct ubifs_info *c, int problem_type,
				struct scanned_file *file, void *priv)
{
	struct invalid_file_problem ifp = {
		.file = file,
		.priv = priv,
	};

	if (FSCK(c)->mode == REBUILD_MODE)
		return;

	fix_problem(c, problem_type, &ifp);
}

static int delete_node(struct ubifs_info *c, const union ubifs_key *key,
		       int lnum, int offs)
{
	int err;

	err = ubifs_tnc_remove_node(c, key, lnum, offs);
	if (err) {
		/* TNC traversing is finished, any TNC path is accessible */
		ubifs_assert(c, !get_failure_reason_callback(c));
	}

	return err;
}

static int delete_dent_nodes(struct ubifs_info *c, struct scanned_file *file,
			     int err)
{
	int ret = 0;
	struct rb_node *this = rb_first(&file->dent_nodes);
	struct scanned_dent_node *dent_node;

	while (this) {
		dent_node = rb_entry(this, struct scanned_dent_node, rb);
		this = rb_next(this);

		if (!err) {
			err = delete_node(c, &dent_node->key,
				dent_node->header.lnum, dent_node->header.offs);
			if (err)
				ret = ret ? ret : err;
		}

		rb_erase(&dent_node->rb, &file->dent_nodes);
		kfree(dent_node);
	}

	return ret;
}

int delete_file(struct ubifs_info *c, struct scanned_file *file)
{
	int err = 0, ret = 0;
	struct rb_node *this;
	struct scanned_file *xattr_file;
	struct scanned_data_node *data_node;

	if (file->ino.header.exist) {
		err = delete_node(c, &file->ino.key, file->ino.header.lnum,
				  file->ino.header.offs);
		if (err)
			ret = ret ? ret : err;
	}

	this = rb_first(&file->data_nodes);
	while (this) {
		data_node = rb_entry(this, struct scanned_data_node, rb);
		this = rb_next(this);

		if (!err) {
			err = delete_node(c, &data_node->key,
				data_node->header.lnum, data_node->header.offs);
			if (err)
				ret = ret ? ret : err;
		}

		rb_erase(&data_node->rb, &file->data_nodes);
		kfree(data_node);
	}

	err = delete_dent_nodes(c, file, err);
	if (err)
		ret = ret ? : err;

	this = rb_first(&file->xattr_files);
	while (this) {
		xattr_file = rb_entry(this, struct scanned_file, rb);
		this = rb_next(this);

		ubifs_assert(c, !rb_first(&xattr_file->xattr_files));
		err = delete_file(c, xattr_file);
		if (err)
			ret = ret ? ret : err;
		rb_erase(&xattr_file->rb, &file->xattr_files);
		kfree(xattr_file);
	}

	return ret;
}

/**
 * insert_xattr_file - insert xattr file into file's subtree.
 * @c: UBIFS file-system description object
 * @xattr_file: xattr file
 * @host_file: host file
 *
 * This inserts xattr file into its' host file's subtree.
 */
static void insert_xattr_file(struct ubifs_info *c,
			      struct scanned_file *xattr_file,
			      struct scanned_file *host_file)
{
	struct scanned_file *tmp_xattr_file;
	struct rb_node **p, *parent = NULL;

	p = &host_file->xattr_files.rb_node;
	while (*p) {
		parent = *p;
		tmp_xattr_file = rb_entry(parent, struct scanned_file, rb);
		if (xattr_file->inum < tmp_xattr_file->inum) {
			p = &(*p)->rb_left;
		} else if (xattr_file->inum > tmp_xattr_file->inum) {
			p = &(*p)->rb_right;
		} else {
			/* Impossible: Same xattr file is inserted twice. */
			ubifs_assert(c, 0);
		}
	}

	rb_link_node(&xattr_file->rb, parent, p);
	rb_insert_color(&xattr_file->rb, &host_file->xattr_files);
}

/**
 * file_is_valid - check whether the file is valid.
 * @c: UBIFS file-system description object
 * @file: file object
 * @file_tree: tree of all scanned files
 * @is_diconnected: reason of invalid file, whether the @file is disconnected
 *
 * This function checks whether given @file is valid, following checks will
 * be performed:
 * 1. All files have none-zero nlink inode, otherwise they are invalid.
 * 2. The file type comes from inode and dentries should be consistent,
 *    inconsistent dentries will be deleted.
 * 3. Directory type or xattr type files only have one dentry. Superfluous
 *    dentries with lower sequence number will be deleted.
 * 4. Non-regular file doesn't have data nodes. Data nodes are deleted for
 *    non-regular file.
 * 5. All files must have at least one dentries, except '/', '/' doesn't
 *    have dentries. Non '/' file is invalid if it doesn't have dentries.
 * 6. Xattr files should have host inode, and host inode cannot be a xattr,
 *    otherwise they are invalid.
 * 7. Encrypted files should have corresponding xattrs, otherwise they are
 *    invalid.
 * Xattr file will be inserted into corresponding host file's subtree.
 *
 * Returns %1 is @file is valid, %0 if @file is invalid, otherwise a negative
 * error code in case of failure.
 * Notice: All xattr files should be traversed before non-xattr files, because
 *         checking item 7 depends on it.
 */
int file_is_valid(struct ubifs_info *c, struct scanned_file *file,
		  struct rb_root *file_tree, int *is_diconnected)
{
	int type;
	struct rb_node *node;
	struct scanned_file *parent_file = NULL;
	struct scanned_dent_node *dent_node;
	struct scanned_data_node *data_node;
	LIST_HEAD(drop_list);

	dbg_fsck("check validation of file %lu, in %s", file->inum, c->dev_name);

	if (!file->ino.header.exist) {
		handle_invalid_file(c, FILE_HAS_NO_INODE, file, NULL);
		return 0;
	}

	if (!file->ino.nlink) {
		handle_invalid_file(c, FILE_HAS_0_NLINK_INODE, file, NULL);
		return 0;
	}

	type = ubifs_get_dent_type(file->ino.mode);

	/* Drop dentry nodes with inconsistent type. */
	for (node = rb_first(&file->dent_nodes); node; node = rb_next(node)) {
		int is_xattr = 0;

		dent_node = rb_entry(node, struct scanned_dent_node, rb);

		if (key_type(c, &dent_node->key) == UBIFS_XENT_KEY)
			is_xattr = 1;
		if (is_xattr != file->ino.is_xattr || type != dent_node->type)
			list_add(&dent_node->list, &drop_list);
	}

	while (!list_empty(&drop_list)) {
		dent_node = list_entry(drop_list.next, struct scanned_dent_node,
				       list);

		handle_invalid_file(c, FILE_HAS_INCONSIST_TYPE, file, dent_node);
		if (FSCK(c)->mode != REBUILD_MODE) {
			int err = delete_node(c, &dent_node->key,
				dent_node->header.lnum, dent_node->header.offs);
			if (err)
				return err;
		}

		list_del(&dent_node->list);
		rb_erase(&dent_node->rb, &file->dent_nodes);
		kfree(dent_node);
	}

	if (type != UBIFS_ITYPE_DIR && !file->ino.is_xattr)
		goto check_data_nodes;

	/* Make sure that directory/xattr type files only have one dentry. */
	node = rb_first(&file->dent_nodes);
	while (node) {
		dent_node = rb_entry(node, struct scanned_dent_node, rb);
		node = rb_next(node);
		if (!node)
			break;

		handle_invalid_file(c, FILE_HAS_TOO_MANY_DENT, file, dent_node);
		if (FSCK(c)->mode != REBUILD_MODE) {
			int err = delete_node(c, &dent_node->key,
				dent_node->header.lnum, dent_node->header.offs);
			if (err)
				return err;
		}

		rb_erase(&dent_node->rb, &file->dent_nodes);
		kfree(dent_node);
	}

check_data_nodes:
	if (type == UBIFS_ITYPE_REG && !file->ino.is_xattr)
		goto check_dent_node;

	/* Make sure that non regular type files not have data/trun nodes. */
	file->trun.header.exist = 0;
	node = rb_first(&file->data_nodes);
	while (node) {
		data_node = rb_entry(node, struct scanned_data_node, rb);
		node = rb_next(node);

		handle_invalid_file(c, FILE_SHOULDNT_HAVE_DATA, file, data_node);
		if (FSCK(c)->mode != REBUILD_MODE) {
			int err = delete_node(c, &data_node->key,
				data_node->header.lnum, data_node->header.offs);
			if (err)
				return err;
		}

		rb_erase(&data_node->rb, &file->data_nodes);
		kfree(data_node);
	}

check_dent_node:
	if (rb_first(&file->dent_nodes)) {
		if (file->inum == UBIFS_ROOT_INO) {
			/* '/' has no dentries. */
			handle_invalid_file(c, FILE_ROOT_HAS_DENT, file,
					    rb_entry(rb_first(&file->dent_nodes),
						struct scanned_dent_node, rb));
			return 0;
		}

		node = rb_first(&file->dent_nodes);
		dent_node = rb_entry(node, struct scanned_dent_node, rb);
		parent_file = lookup_file(file_tree, key_inum(c, &dent_node->key));
	} else {
		/* Non-root files must have dentries. */
		if (file->inum != UBIFS_ROOT_INO) {
			if (type == UBIFS_ITYPE_REG && !file->ino.is_xattr) {
				handle_invalid_file(c, FILE_IS_DISCONNECTED,
						    file, NULL);
				if (is_diconnected)
					*is_diconnected = 1;
			} else {
				handle_invalid_file(c, FILE_HAS_NO_DENT,
						    file, NULL);
			}
			return 0;
		}
	}

	if (file->ino.is_xattr) {
		if (!parent_file) {
			/* Host inode is not found. */
			handle_invalid_file(c, XATTR_HAS_NO_HOST, file, NULL);
			return 0;
		}
		if (parent_file->ino.is_xattr) {
			/* Host cannot be a xattr file. */
			handle_invalid_file(c, XATTR_HAS_WRONG_HOST, file, parent_file);
			return 0;
		}

		insert_xattr_file(c, file, parent_file);
		if (parent_file->ino.is_encrypted) {
			int nlen = min(dent_node->nlen,
				   strlen(UBIFS_XATTR_NAME_ENCRYPTION_CONTEXT));

			if (!strncmp(dent_node->name,
				     UBIFS_XATTR_NAME_ENCRYPTION_CONTEXT, nlen))
				parent_file->has_encrypted_info = true;
		}
	} else {
		if (parent_file && !S_ISDIR(parent_file->ino.mode)) {
			/* Parent file should be directory. */
			if (type == UBIFS_ITYPE_REG) {
				handle_invalid_file(c, FILE_IS_DISCONNECTED,
						    file, NULL);
				if (FSCK(c)->mode != REBUILD_MODE) {
					/* Delete dentries for the disconnected file. */
					int err = delete_dent_nodes(c, file, 0);
					if (err)
						return err;
				}
				if (is_diconnected)
					*is_diconnected = 1;
			}
			return 0;
		}

		/*
		 * Since xattr files are checked in first round, so all
		 * non-xattr files's @has_encrypted_info fields have been
		 * initialized.
		 */
		if (file->ino.is_encrypted && !file->has_encrypted_info) {
			handle_invalid_file(c, FILE_HAS_NO_ENCRYPT, file, NULL);
			return 0;
		}
	}

	return 1;
}

static bool dentry_is_reachable(struct ubifs_info *c,
				struct scanned_dent_node *dent_node,
				struct list_head *path_list,
				struct rb_root *file_tree)
{
	struct scanned_file *parent_file = NULL;
	struct scanned_dent_node *dn, *parent_dent;
	struct rb_node *p;

	/* Check whether the path is cyclical. */
	list_for_each_entry(dn, path_list, list) {
		if (dn == dent_node)
			return false;
	}

	/* Quick path, dentry has already been checked as reachable. */
	if (dent_node->can_be_found)
		return true;

	dent_node->can_be_found = true;
	list_add(&dent_node->list, path_list);

	parent_file = lookup_file(file_tree, key_inum(c, &dent_node->key));
	/* Parent dentry is not found, unreachable. */
	if (!parent_file)
		return false;

	/* Parent dentry is '/', reachable. */
	if (parent_file->inum == UBIFS_ROOT_INO)
		return true;

	p = rb_first(&parent_file->dent_nodes);
	if (!p)
		return false;
	parent_dent = rb_entry(p, struct scanned_dent_node, rb);

	return dentry_is_reachable(c, parent_dent, path_list, file_tree);
}

/**
 * file_is_reachable - whether the file can be found from '/'.
 * @c: UBIFS file-system description object
 * @file: file object
 * @file_tree: tree of all scanned files
 *
 * This function iterates all directory entries in given @file and checks
 * whether each dentry is reachable. All unreachable directory entries will
 * be removed.
 */
bool file_is_reachable(struct ubifs_info *c, struct scanned_file *file,
		       struct rb_root *file_tree)
{
	struct rb_node *node;
	struct scanned_dent_node *dent_node;

	if (file->inum == UBIFS_ROOT_INO)
		goto reachable;

retry:
	for (node = rb_first(&file->dent_nodes); node; node = rb_next(node)) {
		LIST_HEAD(path_list);

		dent_node = rb_entry(node, struct scanned_dent_node, rb);

		if (dentry_is_reachable(c, dent_node, &path_list, file_tree))
			continue;

		while (!list_empty(&path_list)) {
			dent_node = list_entry(path_list.next,
					       struct scanned_dent_node, list);

			handle_invalid_file(c, DENTRY_IS_UNREACHABLE,
					    dent_node->file, dent_node);
			if (FSCK(c)->mode != REBUILD_MODE) {
				int err = delete_node(c, &dent_node->key,
						      dent_node->header.lnum,
						      dent_node->header.offs);
				if (err)
					return err;
			}
			dbg_fsck("remove unreachable dentry %s, in %s",
				 c->encrypted && !file->ino.is_xattr ?
				 "<encrypted>" : dent_node->name, c->dev_name);
			list_del(&dent_node->list);
			rb_erase(&dent_node->rb, &dent_node->file->dent_nodes);
			kfree(dent_node);
		}

		/* Since dentry node is removed from rb-tree, rescan rb-tree. */
		goto retry;
	}

	if (!rb_first(&file->dent_nodes)) {
		if (S_ISREG(file->ino.mode))
			handle_invalid_file(c, FILE_IS_DISCONNECTED, file, NULL);
		else
			handle_invalid_file(c, FILE_HAS_NO_DENT, file, NULL);
		dbg_fsck("file %lu is unreachable, in %s", file->inum, c->dev_name);
		return false;
	}

reachable:
	dbg_fsck("file %lu is reachable, in %s", file->inum, c->dev_name);
	return true;
}

/**
 * calculate_file_info - calculate the information of file
 * @c: UBIFS file-system description object
 * @file: file object
 * @file_tree: tree of all scanned files
 *
 * This function calculates file information according to dentry nodes,
 * data nodes and truncation node. The calculated informaion will be used
 * to correct inode node.
 */
static int calculate_file_info(struct ubifs_info *c, struct scanned_file *file,
			       struct rb_root *file_tree)
{
	int nlink = 0;
	bool corrupted_truncation = false;
	unsigned long long ino_sqnum, trun_size = 0, new_size = 0, trun_sqnum = 0;
	struct rb_node *node;
	struct scanned_file *parent_file, *xattr_file;
	struct scanned_dent_node *dent_node;
	struct scanned_data_node *data_node;
	LIST_HEAD(drop_list);

	for (node = rb_first(&file->xattr_files); node; node = rb_next(node)) {
		xattr_file = rb_entry(node, struct scanned_file, rb);
		dent_node = rb_entry(rb_first(&xattr_file->dent_nodes),
				     struct scanned_dent_node, rb);

		ubifs_assert(c, xattr_file->ino.is_xattr);
		ubifs_assert(c, !rb_first(&xattr_file->xattr_files));
		xattr_file->calc_nlink = 1;
		xattr_file->calc_size = xattr_file->ino.size;

		file->calc_xcnt += 1;
		file->calc_xsz += CALC_DENT_SIZE(dent_node->nlen);
		file->calc_xsz += CALC_XATTR_BYTES(xattr_file->ino.size);
		file->calc_xnms += dent_node->nlen;
	}

	if (file->inum == UBIFS_ROOT_INO) {
		file->calc_nlink += 2;
		file->calc_size += UBIFS_INO_NODE_SZ;
		return 0;
	}

	if (S_ISDIR(file->ino.mode)) {
		file->calc_nlink += 2;
		file->calc_size += UBIFS_INO_NODE_SZ;

		dent_node = rb_entry(rb_first(&file->dent_nodes),
				     struct scanned_dent_node, rb);
		parent_file = lookup_file(file_tree, key_inum(c, &dent_node->key));
		if (!parent_file) {
			ubifs_assert(c, 0);
			return 0;
		}
		parent_file->calc_nlink += 1;
		parent_file->calc_size += CALC_DENT_SIZE(dent_node->nlen);
		return 0;
	}

	for (node = rb_first(&file->dent_nodes); node; node = rb_next(node)) {
		nlink++;

		dent_node = rb_entry(node, struct scanned_dent_node, rb);

		parent_file = lookup_file(file_tree, key_inum(c, &dent_node->key));
		if (!parent_file) {
			ubifs_assert(c, 0);
			return 0;
		}
		parent_file->calc_size += CALC_DENT_SIZE(dent_node->nlen);
	}
	file->calc_nlink = nlink;

	if (!S_ISREG(file->ino.mode)) {
		/* No need to verify i_size for symlink/sock/block/char/fifo. */
		file->calc_size = file->ino.size;
		return 0;
	}

	/*
	 * Process i_size and data content, following situations should
	 * be considered:
	 * 1. Sequential writing or overwriting, i_size should be
	 *    max(i_size, data node size), pick larger sqnum one from
	 *    data nodes with same block index.
	 * 2. Mixed truncation and writing, i_size depends on the latest
	 *    truncation node or inode node or last data node, pick data
	 *    nodes which are not truncated.
	 * 3. Setting bigger i_size attr, pick inode size or biggest
	 *    i_size calculated by data nodes.
	 */
	if (file->trun.header.exist) {
		trun_size = file->trun.new_size;
		trun_sqnum = file->trun.header.sqnum;
	}
	ino_sqnum = file->ino.header.sqnum;
	for (node = rb_first(&file->data_nodes); node; node = rb_next(node)) {
		unsigned long long d_sz, d_sqnum;
		unsigned int block_no;

		data_node = rb_entry(node, struct scanned_data_node, rb);

		d_sqnum = data_node->header.sqnum;
		block_no = key_block(c, &data_node->key);
		d_sz = data_node->size + block_no * UBIFS_BLOCK_SIZE;
		if ((trun_sqnum > d_sqnum && trun_size < d_sz) ||
		    (ino_sqnum > d_sqnum && file->ino.size < d_sz)) {
			/*
			 * The truncated data nodes are not gced after
			 * truncating, just remove them.
			 */
			list_add(&data_node->list, &drop_list);
		} else {
			new_size = max_t(unsigned long long, new_size, d_sz);
		}
	}
	/*
	 * Truncation node is written successful, but inode node is not. It
	 * won't happen because inode node is written before truncation node
	 * according to ubifs_jnl_truncate(), unless only inode is corrupted.
	 * In this case, data nodes could have been removed in history mounting
	 * recovery, so i_size needs to be updated.
	 */
	if (trun_sqnum > ino_sqnum && trun_size < file->ino.size) {
		if (trun_size < new_size) {
			corrupted_truncation = true;
			/*
			 * Appendant writing after truncation and newest inode
			 * is not fell on disk.
			 */
			goto update_isize;
		}

		/*
		 * Overwriting happens after truncation and newest inode is
		 * not fell on disk.
		 */
		file->calc_size = trun_size;
		goto drop_data;
	}
update_isize:
	/*
	 * The file cannot use 'new_size' directly when the file may have ever
	 * been set i_size. For example:
	 *  1. echo 123 > file		# i_size = 4
	 *  2. truncate -s 100 file	# i_size = 100
	 * After scanning, new_size is 4. Apperantly the size of 'file' should
	 * be 100. So, the calculated new_size according to data nodes should
	 * only be used for extending i_size, like ubifs_recover_size() does.
	 */
	if (new_size > file->ino.size || corrupted_truncation)
		file->calc_size = new_size;
	else
		file->calc_size = file->ino.size;

drop_data:
	while (!list_empty(&drop_list)) {
		data_node = list_entry(drop_list.next, struct scanned_data_node,
				       list);

		if (FSCK(c)->mode != REBUILD_MODE) {
			/*
			 * Don't ask, inconsistent file correcting will be
			 * asked in function correct_file_info().
			 */
			int err = delete_node(c, &data_node->key,
				data_node->header.lnum, data_node->header.offs);
			if (err)
				return err;
		}
		list_del(&data_node->list);
		rb_erase(&data_node->rb, &file->data_nodes);
		kfree(data_node);
	}

	return 0;
}

/**
 * correct_file_info - correct the information of file
 * @c: UBIFS file-system description object
 * @file: file object
 *
 * This function corrects file information according to calculated fields,
 * eg. 'calc_nlink', 'calc_xcnt', 'calc_xsz', 'calc_xnms' and 'calc_size'.
 * Corrected inode node will be re-written.
 */
static int correct_file_info(struct ubifs_info *c, struct scanned_file *file)
{
	uint32_t crc;
	int err, lnum, len;
	struct rb_node *node;
	struct ubifs_ino_node *ino;
	struct scanned_file *xattr_file;

	for (node = rb_first(&file->xattr_files); node; node = rb_next(node)) {
		xattr_file = rb_entry(node, struct scanned_file, rb);

		err = correct_file_info(c, xattr_file);
		if (err)
			return err;
	}

	if (file->calc_nlink == file->ino.nlink &&
	    file->calc_xcnt == file->ino.xcnt &&
	    file->calc_xsz == file->ino.xsz &&
	    file->calc_xnms == file->ino.xnms &&
	    file->calc_size == file->ino.size)
		return 0;

	handle_invalid_file(c, FILE_IS_INCONSISTENT, file, NULL);
	lnum = file->ino.header.lnum;
	dbg_fsck("correct file(inum:%lu type:%s), nlink %u->%u, xattr cnt %u->%u, xattr size %u->%u, xattr names %u->%u, size %llu->%llu, at %d:%d, in %s",
		 file->inum, file->ino.is_xattr ? "xattr" :
		 ubifs_get_type_name(ubifs_get_dent_type(file->ino.mode)),
		 file->ino.nlink, file->calc_nlink,
		 file->ino.xcnt, file->calc_xcnt,
		 file->ino.xsz, file->calc_xsz,
		 file->ino.xnms, file->calc_xnms,
		 file->ino.size, file->calc_size,
		 lnum, file->ino.header.offs, c->dev_name);

	err = ubifs_leb_read(c, lnum, c->sbuf, 0, c->leb_size, 0);
	if (err && err != -EBADMSG)
		return err;

	ino = c->sbuf + file->ino.header.offs;
	ino->nlink = cpu_to_le32(file->calc_nlink);
	ino->xattr_cnt = cpu_to_le32(file->calc_xcnt);
	ino->xattr_size = cpu_to_le32(file->calc_xsz);
	ino->xattr_names = cpu_to_le32(file->calc_xnms);
	ino->size = cpu_to_le64(file->calc_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);

	/* Atomically write the fixed LEB back again */
	return ubifs_leb_change(c, lnum, c->sbuf, c->leb_size);
}

/**
 * check_and_correct_files - check and correct information of files.
 * @c: UBIFS file-system description object
 *
 * This function does similar things with dbg_check_filesystem(), besides,
 * it also corrects file information if the calculated information is not
 * consistent with information from flash.
 */
int check_and_correct_files(struct ubifs_info *c)
{
	int err;
	struct rb_node *node;
	struct scanned_file *file;
	struct rb_root *tree = &FSCK(c)->scanned_files;

	for (node = rb_first(tree); node; node = rb_next(node)) {
		file = rb_entry(node, struct scanned_file, rb);

		err = calculate_file_info(c, file, tree);
		if (err)
			return err;
	}

	for (node = rb_first(tree); node; node = rb_next(node)) {
		file = rb_entry(node, struct scanned_file, rb);

		err = correct_file_info(c, file);
		if (err)
			return err;
	}

	if (list_empty(&FSCK(c)->disconnected_files))
		return 0;

	ubifs_assert(c, FSCK(c)->mode != REBUILD_MODE);
	list_for_each_entry(file, &FSCK(c)->disconnected_files, list) {
		err = calculate_file_info(c, file, tree);
		if (err)
			return err;

		/* Reset disconnected file's nlink as one. */
		file->calc_nlink = 1;
		err = correct_file_info(c, file);
		if (err)
			return err;
	}

	return 0;
}
