blob: d25cfb53e75d9c5f7d69bc4fcdf67b3deee35f6e [file] [log] [blame]
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2020 Bootlin
4 *
5 * Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
6 */
7
8#include <asm/unaligned.h>
Pali Rohár11f5c0c2021-03-03 10:52:36 +01009#include <compiler.h>
Joao Marcos Costa29da3742020-07-30 15:33:47 +020010#include <errno.h>
11#include <stdint.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15
16#include "sqfs_decompressor.h"
17#include "sqfs_filesystem.h"
18#include "sqfs_utils.h"
19
20int sqfs_inode_size(struct squashfs_base_inode *inode, u32 blk_size)
21{
22 switch (get_unaligned_le16(&inode->inode_type)) {
23 case SQFS_DIR_TYPE:
24 return sizeof(struct squashfs_dir_inode);
25
26 case SQFS_REG_TYPE: {
27 struct squashfs_reg_inode *reg =
28 (struct squashfs_reg_inode *)inode;
29 u32 fragment = get_unaligned_le32(&reg->fragment);
30 u32 file_size = get_unaligned_le32(&reg->file_size);
31 unsigned int blk_list_size;
32
33 if (SQFS_IS_FRAGMENTED(fragment))
34 blk_list_size = file_size / blk_size;
35 else
36 blk_list_size = DIV_ROUND_UP(file_size, blk_size);
37
38 return sizeof(*reg) + blk_list_size * sizeof(u32);
39 }
40
41 case SQFS_LDIR_TYPE: {
42 struct squashfs_ldir_inode *ldir =
43 (struct squashfs_ldir_inode *)inode;
44 u16 i_count = get_unaligned_le16(&ldir->i_count);
45 unsigned int index_list_size = 0, l = 0;
46 struct squashfs_directory_index *di;
47 u32 sz;
48
49 if (i_count == 0)
50 return sizeof(*ldir);
51
52 di = ldir->index;
Gerard Koskamp6f153702020-10-30 13:41:58 +000053 while (l < i_count) {
Joao Marcos Costa29da3742020-07-30 15:33:47 +020054 sz = get_unaligned_le32(&di->size) + 1;
55 index_list_size += sz;
56 di = (void *)di + sizeof(*di) + sz;
57 l++;
58 }
59
60 return sizeof(*ldir) + index_list_size +
Gerard Koskamp6f153702020-10-30 13:41:58 +000061 i_count * SQFS_DIR_INDEX_BASE_LENGTH;
Joao Marcos Costa29da3742020-07-30 15:33:47 +020062 }
63
64 case SQFS_LREG_TYPE: {
65 struct squashfs_lreg_inode *lreg =
66 (struct squashfs_lreg_inode *)inode;
67 u32 fragment = get_unaligned_le32(&lreg->fragment);
68 u64 file_size = get_unaligned_le64(&lreg->file_size);
69 unsigned int blk_list_size;
70
71 if (fragment == 0xFFFFFFFF)
72 blk_list_size = DIV_ROUND_UP(file_size, blk_size);
73 else
74 blk_list_size = file_size / blk_size;
75
76 return sizeof(*lreg) + blk_list_size * sizeof(u32);
77 }
78
79 case SQFS_SYMLINK_TYPE:
80 case SQFS_LSYMLINK_TYPE: {
81 struct squashfs_symlink_inode *symlink =
82 (struct squashfs_symlink_inode *)inode;
83
84 return sizeof(*symlink) +
85 get_unaligned_le32(&symlink->symlink_size);
86 }
87
88 case SQFS_BLKDEV_TYPE:
89 case SQFS_CHRDEV_TYPE:
90 return sizeof(struct squashfs_dev_inode);
91 case SQFS_LBLKDEV_TYPE:
92 case SQFS_LCHRDEV_TYPE:
93 return sizeof(struct squashfs_ldev_inode);
94 case SQFS_FIFO_TYPE:
95 case SQFS_SOCKET_TYPE:
96 return sizeof(struct squashfs_ipc_inode);
97 case SQFS_LFIFO_TYPE:
98 case SQFS_LSOCKET_TYPE:
99 return sizeof(struct squashfs_lipc_inode);
100 default:
101 printf("Error while searching inode: unknown type.\n");
102 return -EINVAL;
103 }
104}
105
106/*
107 * Given the uncompressed inode table, the inode to be found and the number of
108 * inodes in the table, return inode position in case of success.
109 */
110void *sqfs_find_inode(void *inode_table, int inode_number, __le32 inode_count,
111 __le32 block_size)
112{
113 struct squashfs_base_inode *base;
114 unsigned int offset = 0, k;
115 int sz;
116
117 if (!inode_table) {
118 printf("%s: Invalid pointer to inode table.\n", __func__);
119 return NULL;
120 }
121
122 for (k = 0; k < le32_to_cpu(inode_count); k++) {
123 base = inode_table + offset;
124 if (get_unaligned_le32(&base->inode_number) == inode_number)
125 return inode_table + offset;
126
127 sz = sqfs_inode_size(base, le32_to_cpu(block_size));
128 if (sz < 0)
129 return NULL;
130
131 offset += sz;
132 }
133
134 printf("Inode not found.\n");
135
136 return NULL;
137}
138
139int sqfs_read_metablock(unsigned char *file_mapping, int offset,
140 bool *compressed, u32 *data_size)
141{
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +0200142 const unsigned char *data;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200143 u16 header;
144
Heinrich Schuchardt3abb7a42020-09-23 21:13:51 +0200145 if (!file_mapping)
Joao Marcos Costaf1dfe4a2020-09-11 12:21:06 +0200146 return -EFAULT;
Heinrich Schuchardt3abb7a42020-09-23 21:13:51 +0200147 data = file_mapping + offset;
Joao Marcos Costaf1dfe4a2020-09-11 12:21:06 +0200148
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200149 header = get_unaligned((u16 *)data);
Joao Marcos Costaf1dfe4a2020-09-11 12:21:06 +0200150 if (!header)
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +0200151 return -EINVAL;
152
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200153 *compressed = SQFS_COMPRESSED_METADATA(header);
154 *data_size = SQFS_METADATA_SIZE(header);
155
156 if (*data_size > SQFS_METADATA_BLOCK_SIZE) {
157 printf("Invalid metatada block size: %d bytes.\n", *data_size);
158 return -EINVAL;
159 }
160
161 return 0;
162}