diff mbox series

[v2] squashfs: Fix file lookups on appended images

Message ID 6b273159-fea1-27df-6b21-a86eb9642af8@snapit.group
State Deferred
Delegated to: Tom Rini
Headers show
Series [v2] squashfs: Fix file lookups on appended images | expand

Commit Message

Campbell Suter Dec. 15, 2021, 10:11 p.m. UTC
Previously, sqfs_search_dir assumed that the root inode has the highest
available ID. Unfortunately, this fails on appended images (those where
you run mksquashfs multiple times) since the inodes there are arranged
differently to those on a regular image.

The squashfs format does contain a reference to the root inode.
Unfortunately, it's stored as it's compressed block address and a byte
offset into that block[1]. The only place we have access to that
information is in sqfs_read_inode_table, so while it's ugly we find the
root inode there and pass it back for later use.

V2 of this patch fixes sqfs_opendir directly accessing an inode field, and
instead uses the unaligned access function as it should.

[1] In https://github.com/plougher/squashfs-tools/tree/b6f80b53:
     squashfs-tools/squashfs_fs.h line 154
     squashfs-tools/read_fs.c line 950

Signed-off-by: Campbell Suter <campbell@snapit.group>
---
  fs/squashfs/sqfs.c | 65 +++++++++++++++++++++++++++++++++++++++-------
  1 file changed, 56 insertions(+), 9 deletions(-)
diff mbox series

Patch

diff --git a/fs/squashfs/sqfs.c b/fs/squashfs/sqfs.c
index e2d91c654c..5c05ddca38 100644
--- a/fs/squashfs/sqfs.c
+++ b/fs/squashfs/sqfs.c
@@ -442,7 +442,7 @@  static char *sqfs_resolve_symlink(struct squashfs_symlink_inode *sym,
   * table.
   */
  static int sqfs_search_dir(struct squashfs_dir_stream *dirs, char **token_list,
-			   int token_count, u32 *m_list, int m_count)
+			   int token_count, u32 *m_list, int m_count, int root_inode)
  {
  	struct squashfs_super_block *sblk = ctxt.sblk;
  	char *path, *target, **sym_tokens, *res, *rem;
@@ -463,7 +463,7 @@  static int sqfs_search_dir(struct squashfs_dir_stream *dirs, char **token_list,
  	dirsp = (struct fs_dir_stream *)dirs;
  
  	/* Start by root inode */
-	table = sqfs_find_inode(dirs->inode_table, le32_to_cpu(sblk->inodes),
+	table = sqfs_find_inode(dirs->inode_table, root_inode,
  				sblk->inodes, sblk->block_size);
  
  	dir = (struct squashfs_dir_inode *)table;
@@ -576,7 +576,7 @@  static int sqfs_search_dir(struct squashfs_dir_stream *dirs, char **token_list,
  			dirs->entry = NULL;
  
  			ret = sqfs_search_dir(dirs, sym_tokens, token_count,
-					      m_list, m_count);
+					      m_list, m_count, root_inode);
  			goto out;
  		} else if (!sqfs_is_dir(get_unaligned_le16(&dir->inode_type))) {
  			printf("** Cannot find directory. **\n");
@@ -684,15 +684,17 @@  static int sqfs_get_metablk_pos(u32 *pos_list, void *table, u32 offset,
  	return ret;
  }
  
-static int sqfs_read_inode_table(unsigned char **inode_table)
+static int sqfs_read_inode_table(unsigned char **inode_table,
+				 const struct squashfs_base_inode **root_inode)
  {
  	struct squashfs_super_block *sblk = ctxt.sblk;
-	u64 start, n_blks, table_offset, table_size;
+	u64 start, n_blks, table_offset, src_table_pos = 0, 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;
+	u64 root_inode_block, root_inode_offset;
  
  	table_size = get_unaligned_le64(&sblk->directory_table_start) -
  		get_unaligned_le64(&sblk->inode_table_start);
@@ -701,6 +703,13 @@  static int sqfs_read_inode_table(unsigned char **inode_table)
  	n_blks = sqfs_calc_n_blks(sblk->inode_table_start,
  				  sblk->directory_table_start, &table_offset);
  
+	/*
+	 * Value to indicate 'not found' - set it here so that it's not unset
+	 * in case of an error.
+	 */
+	if (root_inode)
+		*root_inode = NULL;
+
  	/* Allocate a proper sized buffer (itb) to store the inode table */
  	itb = malloc_cache_aligned(n_blks * ctxt.cur_dev->blksz);
  	if (!itb)
@@ -733,8 +742,34 @@  static int sqfs_read_inode_table(unsigned char **inode_table)
  
  	src_table = itb + table_offset + SQFS_HEADER_SIZE;
  
+	/*
+	 * While we're here, find the root inode. It's specified by an on-disk
+	 * block position and offset, which we can't look up anywhere else if
+	 * the metadata is compressed. Thus find it's inode index, which can
+	 * later be used to look it up.
+	 *
+	 * Note that while directory entries also have these offset
+	 * parameters, they also include the inode number which is what this
+	 * implementation uses to look them up.
+	 */
+	root_inode_block = (u64)le64_to_cpu(sblk->root_inode) >> 16;
+	root_inode_offset = (u64)le64_to_cpu(sblk->root_inode) & 0xffff;
+
  	/* Extract compressed Inode table */
  	for (j = 0; j < metablks_count; j++) {
+		/*
+		 * Check if we're at the raw block offset the root inode is
+		 * specified to live at, and if so point the root inode to
+		 * where it's about to end up.
+		 *
+		 * Note we're doing this now, before dest_offset has been
+		 * pointed to the next block.
+		 */
+		if (src_table_pos == root_inode_block && root_inode) {
+			*root_inode = (const struct squashfs_base_inode *)
+			    (*inode_table + dest_offset + root_inode_offset);
+		}
+
  		sqfs_read_metablock(itb, table_offset, &compressed, &src_len);
  		if (compressed) {
  			dest_len = SQFS_METADATA_BLOCK_SIZE;
@@ -749,8 +784,8 @@  static int sqfs_read_inode_table(unsigned char **inode_table)
  
  			dest_offset += dest_len;
  		} else {
-			memcpy(*inode_table + (j * SQFS_METADATA_BLOCK_SIZE),
-			       src_table, src_len);
+			memcpy(*inode_table + dest_offset, src_table, src_len);
+			dest_offset += SQFS_METADATA_BLOCK_SIZE;
  		}
  
  		/*
@@ -760,6 +795,17 @@  static int sqfs_read_inode_table(unsigned char **inode_table)
  
  		table_offset += src_len + SQFS_HEADER_SIZE;
  		src_table += src_len + SQFS_HEADER_SIZE;
+		src_table_pos += src_len + SQFS_HEADER_SIZE;
+	}
+
+	/*
+	 * If we couldn't find the inode, return an error to reduce the chance
+	 * someone will try to index this null pointer.
+	 */
+	if (root_inode && !*root_inode) {
+		printf("Failed to locate root squashfs inode.\n");
+		ret = -ENOENT;
+		goto free_itb;
  	}
  
  free_itb:
@@ -875,6 +921,7 @@  int sqfs_opendir(const char *filename, struct fs_dir_stream **dirsp)
  	struct squashfs_dir_stream *dirs;
  	char **token_list = NULL, *path = NULL;
  	u32 *pos_list = NULL;
+	const struct squashfs_base_inode *root_inode = NULL;
  
  	dirs = calloc(1, sizeof(*dirs));
  	if (!dirs)
@@ -887,7 +934,7 @@  int sqfs_opendir(const char *filename, struct fs_dir_stream **dirsp)
  	dirs->inode_table = NULL;
  	dirs->dir_table = NULL;
  
-	ret = sqfs_read_inode_table(&inode_table);
+	ret = sqfs_read_inode_table(&inode_table, &root_inode);
  	if (ret) {
  		ret = -EINVAL;
  		goto out;
@@ -929,7 +976,7 @@  int sqfs_opendir(const char *filename, struct fs_dir_stream **dirsp)
  	dirs->inode_table = inode_table;
  	dirs->dir_table = dir_table;
  	ret = sqfs_search_dir(dirs, token_list, token_count, pos_list,
-			      metablks_count);
+			      metablks_count, (int)get_unaligned_le32(&root_inode->inode_number));
  	if (ret)
  		goto out;