Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e3c3efc2 authored by Artem Bityutskiy's avatar Artem Bityutskiy
Browse files

UBIFS: add inode size debugging check



Add one more check to UBIFS - a check that makes sure that there
are no data nodes beyond inode size. And few commantaries fixes
along the line.

Signed-off-by: default avatarArtem Bityutskiy <Artem.Bityutskiy@nokia.com>
Reviewed-by: default avatarAdrian Hunter <Adrian.Hunter@nokia.com>
parent 8c6866b0
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -321,6 +321,8 @@ void dbg_check_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap, int cat,
int dbg_check_lprops(struct ubifs_info *c);
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);

/* Force the use of in-the-gaps method for testing */

@@ -460,6 +462,7 @@ void dbg_debugfs_exit_fs(struct ubifs_info *c);
#define dbg_check_heap(c, heap, cat, add_pos)      ({})
#define dbg_check_lprops(c)                        0
#define dbg_check_lpt_nodes(c, cnode, row, col)    0
#define dbg_check_inode_size(c, inode, size)       0
#define dbg_force_in_the_gaps_enabled              0
#define dbg_force_in_the_gaps()                    0
#define dbg_failure_mode                           0
+13 −0
Original line number Diff line number Diff line
@@ -277,6 +277,18 @@ static inline void data_key_init(const struct ubifs_info *c,
	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
@@ -518,4 +530,5 @@ static inline unsigned long long key_max_inode_size(const struct ubifs_info *c)
		return 0;
	}
}

#endif /* !__UBIFS_KEY_H__ */
+2 −0
Original line number Diff line number Diff line
@@ -317,6 +317,8 @@ static int ubifs_write_inode(struct inode *inode, int wait)
		if (err)
			ubifs_err("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;
+73 −3
Original line number Diff line number Diff line
@@ -1159,7 +1159,7 @@ static struct ubifs_znode *dirty_cow_bottom_up(struct ubifs_info *c,
 *   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
 *     @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.
@@ -1433,7 +1433,7 @@ static int maybe_leb_gced(struct ubifs_info *c, int lnum, int gc_seq1)
 * @lnum: LEB number is returned here
 * @offs: offset is returned here
 *
 * This function look up and reads node with key @key. The caller has to make
 * 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.
@@ -3268,3 +3268,73 @@ int ubifs_dirty_idx_node(struct ubifs_info *c, union ubifs_key *key, int level,
	mutex_unlock(&c->tnc_mutex);
	return err;
}

#ifdef CONFIG_UBIFS_FS_DEBUG

/**
 * 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 (!(ubifs_chk_flags & UBIFS_CHK_GEN))
		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) {
		err = -EINVAL;
		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("inode %lu has size %lld, but there are data at offset %lld "
		  "(data key %s)", (unsigned long)inode->i_ino, size,
		  ((loff_t)block) << UBIFS_BLOCK_SHIFT, DBGKEY(key));
	dbg_dump_inode(c, inode);
	dbg_dump_stack();
	err = -EINVAL;

out_unlock:
	mutex_unlock(&c->tnc_mutex);
	return err;
}

#endif /* CONFIG_UBIFS_FS_DEBUG */