blob: ce9a8ff8e2a49349e628accf07bc81ada76465a7 [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{
Norbert van Bolhuisabd98d02024-12-24 01:49:04 +010022 u16 inode_type = get_unaligned_le16(&inode->inode_type);
23
24 switch (inode_type) {
Joao Marcos Costa29da3742020-07-30 15:33:47 +020025 case SQFS_DIR_TYPE:
26 return sizeof(struct squashfs_dir_inode);
27
28 case SQFS_REG_TYPE: {
29 struct squashfs_reg_inode *reg =
30 (struct squashfs_reg_inode *)inode;
31 u32 fragment = get_unaligned_le32(&reg->fragment);
32 u32 file_size = get_unaligned_le32(&reg->file_size);
33 unsigned int blk_list_size;
34
35 if (SQFS_IS_FRAGMENTED(fragment))
36 blk_list_size = file_size / blk_size;
37 else
38 blk_list_size = DIV_ROUND_UP(file_size, blk_size);
39
40 return sizeof(*reg) + blk_list_size * sizeof(u32);
41 }
42
43 case SQFS_LDIR_TYPE: {
44 struct squashfs_ldir_inode *ldir =
45 (struct squashfs_ldir_inode *)inode;
46 u16 i_count = get_unaligned_le16(&ldir->i_count);
47 unsigned int index_list_size = 0, l = 0;
48 struct squashfs_directory_index *di;
49 u32 sz;
50
51 if (i_count == 0)
52 return sizeof(*ldir);
53
54 di = ldir->index;
Gerard Koskamp6f153702020-10-30 13:41:58 +000055 while (l < i_count) {
Joao Marcos Costa29da3742020-07-30 15:33:47 +020056 sz = get_unaligned_le32(&di->size) + 1;
57 index_list_size += sz;
58 di = (void *)di + sizeof(*di) + sz;
59 l++;
60 }
61
62 return sizeof(*ldir) + index_list_size +
Gerard Koskamp6f153702020-10-30 13:41:58 +000063 i_count * SQFS_DIR_INDEX_BASE_LENGTH;
Joao Marcos Costa29da3742020-07-30 15:33:47 +020064 }
65
66 case SQFS_LREG_TYPE: {
67 struct squashfs_lreg_inode *lreg =
68 (struct squashfs_lreg_inode *)inode;
69 u32 fragment = get_unaligned_le32(&lreg->fragment);
70 u64 file_size = get_unaligned_le64(&lreg->file_size);
71 unsigned int blk_list_size;
72
73 if (fragment == 0xFFFFFFFF)
74 blk_list_size = DIV_ROUND_UP(file_size, blk_size);
75 else
76 blk_list_size = file_size / blk_size;
77
78 return sizeof(*lreg) + blk_list_size * sizeof(u32);
79 }
80
81 case SQFS_SYMLINK_TYPE:
82 case SQFS_LSYMLINK_TYPE: {
Richard Weinberger16a19a62024-08-02 18:36:45 +020083 int size;
84
Joao Marcos Costa29da3742020-07-30 15:33:47 +020085 struct squashfs_symlink_inode *symlink =
86 (struct squashfs_symlink_inode *)inode;
87
Richard Weinberger16a19a62024-08-02 18:36:45 +020088 if (__builtin_add_overflow(sizeof(*symlink),
89 get_unaligned_le32(&symlink->symlink_size), &size))
90 return -EINVAL;
91
Norbert van Bolhuisabd98d02024-12-24 01:49:04 +010092 return (inode_type == SQFS_SYMLINK_TYPE) ? size : size + sizeof(u32);
Joao Marcos Costa29da3742020-07-30 15:33:47 +020093 }
94
95 case SQFS_BLKDEV_TYPE:
96 case SQFS_CHRDEV_TYPE:
97 return sizeof(struct squashfs_dev_inode);
98 case SQFS_LBLKDEV_TYPE:
99 case SQFS_LCHRDEV_TYPE:
100 return sizeof(struct squashfs_ldev_inode);
101 case SQFS_FIFO_TYPE:
102 case SQFS_SOCKET_TYPE:
103 return sizeof(struct squashfs_ipc_inode);
104 case SQFS_LFIFO_TYPE:
105 case SQFS_LSOCKET_TYPE:
106 return sizeof(struct squashfs_lipc_inode);
107 default:
108 printf("Error while searching inode: unknown type.\n");
109 return -EINVAL;
110 }
111}
112
113/*
114 * Given the uncompressed inode table, the inode to be found and the number of
115 * inodes in the table, return inode position in case of success.
116 */
117void *sqfs_find_inode(void *inode_table, int inode_number, __le32 inode_count,
118 __le32 block_size)
119{
120 struct squashfs_base_inode *base;
121 unsigned int offset = 0, k;
122 int sz;
123
124 if (!inode_table) {
125 printf("%s: Invalid pointer to inode table.\n", __func__);
126 return NULL;
127 }
128
129 for (k = 0; k < le32_to_cpu(inode_count); k++) {
130 base = inode_table + offset;
131 if (get_unaligned_le32(&base->inode_number) == inode_number)
132 return inode_table + offset;
133
134 sz = sqfs_inode_size(base, le32_to_cpu(block_size));
135 if (sz < 0)
136 return NULL;
137
138 offset += sz;
139 }
140
141 printf("Inode not found.\n");
142
143 return NULL;
144}
145
146int sqfs_read_metablock(unsigned char *file_mapping, int offset,
147 bool *compressed, u32 *data_size)
148{
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +0200149 const unsigned char *data;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200150 u16 header;
151
Heinrich Schuchardt3abb7a42020-09-23 21:13:51 +0200152 if (!file_mapping)
Joao Marcos Costaf1dfe4a2020-09-11 12:21:06 +0200153 return -EFAULT;
Heinrich Schuchardt3abb7a42020-09-23 21:13:51 +0200154 data = file_mapping + offset;
Joao Marcos Costaf1dfe4a2020-09-11 12:21:06 +0200155
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200156 header = get_unaligned((u16 *)data);
Joao Marcos Costaf1dfe4a2020-09-11 12:21:06 +0200157 if (!header)
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +0200158 return -EINVAL;
159
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200160 *compressed = SQFS_COMPRESSED_METADATA(header);
161 *data_size = SQFS_METADATA_SIZE(header);
162
163 if (*data_size > SQFS_METADATA_BLOCK_SIZE) {
164 printf("Invalid metatada block size: %d bytes.\n", *data_size);
165 return -EINVAL;
166 }
167
168 return 0;
169}