1 /*
2 * Unsquash a squashfs filesystem. This is a highly compressed read only
3 * filesystem.
4 *
5 * Copyright (c) 2009, 2010, 2011, 2012, 2013
6 * Phillip Lougher <phillip@squashfs.org.uk>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2,
11 * or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 *
22 * unsquash-4.c
23 */
24
25 #include "unsquashfs.h"
26 #include "squashfs_swap.h"
27
28 static struct squashfs_fragment_entry *fragment_table;
29 static unsigned int *id_table;
30
read_fragment_table_4(long long * directory_table_end)31 int read_fragment_table_4(long long *directory_table_end)
32 {
33 int res, i;
34 int bytes = SQUASHFS_FRAGMENT_BYTES(sBlk.s.fragments);
35 int indexes = SQUASHFS_FRAGMENT_INDEXES(sBlk.s.fragments);
36 long long fragment_table_index[indexes];
37
38 TRACE("read_fragment_table: %d fragments, reading %d fragment indexes "
39 "from 0x%llx\n", sBlk.s.fragments, indexes,
40 sBlk.s.fragment_table_start);
41
42 if(sBlk.s.fragments == 0) {
43 *directory_table_end = sBlk.s.fragment_table_start;
44 return TRUE;
45 }
46
47 fragment_table = malloc(bytes);
48 if(fragment_table == NULL)
49 EXIT_UNSQUASH("read_fragment_table: failed to allocate "
50 "fragment table\n");
51
52 res = read_fs_bytes(fd, sBlk.s.fragment_table_start,
53 SQUASHFS_FRAGMENT_INDEX_BYTES(sBlk.s.fragments),
54 fragment_table_index);
55 if(res == FALSE) {
56 ERROR("read_fragment_table: failed to read fragment table "
57 "index\n");
58 return FALSE;
59 }
60 SQUASHFS_INSWAP_FRAGMENT_INDEXES(fragment_table_index, indexes);
61
62 for(i = 0; i < indexes; i++) {
63 int expected = (i + 1) != indexes ? SQUASHFS_METADATA_SIZE :
64 bytes & (SQUASHFS_METADATA_SIZE - 1);
65 int length = read_block(fd, fragment_table_index[i], NULL,
66 expected, ((char *) fragment_table) + (i *
67 SQUASHFS_METADATA_SIZE));
68 TRACE("Read fragment table block %d, from 0x%llx, length %d\n",
69 i, fragment_table_index[i], length);
70 if(length == FALSE) {
71 ERROR("read_fragment_table: failed to read fragment "
72 "table index\n");
73 return FALSE;
74 }
75 }
76
77 for(i = 0; i < sBlk.s.fragments; i++)
78 SQUASHFS_INSWAP_FRAGMENT_ENTRY(&fragment_table[i]);
79
80 *directory_table_end = fragment_table_index[0];
81 return TRUE;
82 }
83
84
read_fragment_4(unsigned int fragment,long long * start_block,int * size)85 void read_fragment_4(unsigned int fragment, long long *start_block, int *size)
86 {
87 TRACE("read_fragment: reading fragment %d\n", fragment);
88
89 struct squashfs_fragment_entry *fragment_entry;
90
91 fragment_entry = &fragment_table[fragment];
92 *start_block = fragment_entry->start_block;
93 *size = fragment_entry->size;
94 }
95
96
read_inode_4(unsigned int start_block,unsigned int offset)97 struct inode *read_inode_4(unsigned int start_block, unsigned int offset)
98 {
99 static union squashfs_inode_header header;
100 long long start = sBlk.s.inode_table_start + start_block;
101 int bytes = lookup_entry(inode_table_hash, start);
102 char *block_ptr = inode_table + bytes + offset;
103 static struct inode i;
104
105 TRACE("read_inode: reading inode [%d:%d]\n", start_block, offset);
106
107 if(bytes == -1)
108 EXIT_UNSQUASH("read_inode: inode table block %lld not found\n",
109 start);
110
111 SQUASHFS_SWAP_BASE_INODE_HEADER(block_ptr, &header.base);
112
113 i.uid = (uid_t) id_table[header.base.uid];
114 i.gid = (uid_t) id_table[header.base.guid];
115 i.mode = lookup_type[header.base.inode_type] | header.base.mode;
116 i.type = header.base.inode_type;
117 i.time = header.base.mtime;
118 i.inode_number = header.base.inode_number;
119
120 switch(header.base.inode_type) {
121 case SQUASHFS_DIR_TYPE: {
122 struct squashfs_dir_inode_header *inode = &header.dir;
123
124 SQUASHFS_SWAP_DIR_INODE_HEADER(block_ptr, inode);
125
126 i.data = inode->file_size;
127 i.offset = inode->offset;
128 i.start = inode->start_block;
129 i.xattr = SQUASHFS_INVALID_XATTR;
130 break;
131 }
132 case SQUASHFS_LDIR_TYPE: {
133 struct squashfs_ldir_inode_header *inode = &header.ldir;
134
135 SQUASHFS_SWAP_LDIR_INODE_HEADER(block_ptr, inode);
136
137 i.data = inode->file_size;
138 i.offset = inode->offset;
139 i.start = inode->start_block;
140 i.xattr = inode->xattr;
141 break;
142 }
143 case SQUASHFS_FILE_TYPE: {
144 struct squashfs_reg_inode_header *inode = &header.reg;
145
146 SQUASHFS_SWAP_REG_INODE_HEADER(block_ptr, inode);
147
148 i.data = inode->file_size;
149 i.frag_bytes = inode->fragment == SQUASHFS_INVALID_FRAG
150 ? 0 : inode->file_size % sBlk.s.block_size;
151 i.fragment = inode->fragment;
152 i.offset = inode->offset;
153 i.blocks = inode->fragment == SQUASHFS_INVALID_FRAG ?
154 (i.data + sBlk.s.block_size - 1) >>
155 sBlk.s.block_log :
156 i.data >> sBlk.s.block_log;
157 i.start = inode->start_block;
158 i.sparse = 0;
159 i.block_ptr = block_ptr + sizeof(*inode);
160 i.xattr = SQUASHFS_INVALID_XATTR;
161 break;
162 }
163 case SQUASHFS_LREG_TYPE: {
164 struct squashfs_lreg_inode_header *inode = &header.lreg;
165
166 SQUASHFS_SWAP_LREG_INODE_HEADER(block_ptr, inode);
167
168 i.data = inode->file_size;
169 i.frag_bytes = inode->fragment == SQUASHFS_INVALID_FRAG
170 ? 0 : inode->file_size % sBlk.s.block_size;
171 i.fragment = inode->fragment;
172 i.offset = inode->offset;
173 i.blocks = inode->fragment == SQUASHFS_INVALID_FRAG ?
174 (inode->file_size + sBlk.s.block_size - 1) >>
175 sBlk.s.block_log :
176 inode->file_size >> sBlk.s.block_log;
177 i.start = inode->start_block;
178 i.sparse = inode->sparse != 0;
179 i.block_ptr = block_ptr + sizeof(*inode);
180 i.xattr = inode->xattr;
181 break;
182 }
183 case SQUASHFS_SYMLINK_TYPE:
184 case SQUASHFS_LSYMLINK_TYPE: {
185 struct squashfs_symlink_inode_header *inode = &header.symlink;
186
187 SQUASHFS_SWAP_SYMLINK_INODE_HEADER(block_ptr, inode);
188
189 i.symlink = malloc(inode->symlink_size + 1);
190 if(i.symlink == NULL)
191 EXIT_UNSQUASH("read_inode: failed to malloc "
192 "symlink data\n");
193 strncpy(i.symlink, block_ptr +
194 sizeof(struct squashfs_symlink_inode_header),
195 inode->symlink_size);
196 i.symlink[inode->symlink_size] = '\0';
197 i.data = inode->symlink_size;
198
199 if(header.base.inode_type == SQUASHFS_LSYMLINK_TYPE)
200 SQUASHFS_SWAP_INTS(block_ptr +
201 sizeof(struct squashfs_symlink_inode_header) +
202 inode->symlink_size, &i.xattr, 1);
203 else
204 i.xattr = SQUASHFS_INVALID_XATTR;
205 break;
206 }
207 case SQUASHFS_BLKDEV_TYPE:
208 case SQUASHFS_CHRDEV_TYPE: {
209 struct squashfs_dev_inode_header *inode = &header.dev;
210
211 SQUASHFS_SWAP_DEV_INODE_HEADER(block_ptr, inode);
212
213 i.data = inode->rdev;
214 i.xattr = SQUASHFS_INVALID_XATTR;
215 break;
216 }
217 case SQUASHFS_LBLKDEV_TYPE:
218 case SQUASHFS_LCHRDEV_TYPE: {
219 struct squashfs_ldev_inode_header *inode = &header.ldev;
220
221 SQUASHFS_SWAP_LDEV_INODE_HEADER(block_ptr, inode);
222
223 i.data = inode->rdev;
224 i.xattr = inode->xattr;
225 break;
226 }
227 case SQUASHFS_FIFO_TYPE:
228 case SQUASHFS_SOCKET_TYPE:
229 i.data = 0;
230 i.xattr = SQUASHFS_INVALID_XATTR;
231 break;
232 case SQUASHFS_LFIFO_TYPE:
233 case SQUASHFS_LSOCKET_TYPE: {
234 struct squashfs_lipc_inode_header *inode = &header.lipc;
235
236 SQUASHFS_SWAP_LIPC_INODE_HEADER(block_ptr, inode);
237
238 i.data = 0;
239 i.xattr = inode->xattr;
240 break;
241 }
242 default:
243 EXIT_UNSQUASH("Unknown inode type %d in read_inode!\n",
244 header.base.inode_type);
245 }
246 return &i;
247 }
248
249
squashfs_opendir_4(unsigned int block_start,unsigned int offset,struct inode ** i)250 struct dir *squashfs_opendir_4(unsigned int block_start, unsigned int offset,
251 struct inode **i)
252 {
253 struct squashfs_dir_header dirh;
254 char buffer[sizeof(struct squashfs_dir_entry) + SQUASHFS_NAME_LEN + 1]
255 __attribute__((aligned));
256 struct squashfs_dir_entry *dire = (struct squashfs_dir_entry *) buffer;
257 long long start;
258 int bytes;
259 int dir_count, size;
260 struct dir_ent *new_dir;
261 struct dir *dir;
262
263 TRACE("squashfs_opendir: inode start block %d, offset %d\n",
264 block_start, offset);
265
266 *i = s_ops.read_inode(block_start, offset);
267
268 dir = malloc(sizeof(struct dir));
269 if(dir == NULL)
270 EXIT_UNSQUASH("squashfs_opendir: malloc failed!\n");
271
272 dir->dir_count = 0;
273 dir->cur_entry = 0;
274 dir->mode = (*i)->mode;
275 dir->uid = (*i)->uid;
276 dir->guid = (*i)->gid;
277 dir->mtime = (*i)->time;
278 dir->xattr = (*i)->xattr;
279 dir->dirs = NULL;
280
281 if ((*i)->data == 3)
282 /*
283 * if the directory is empty, skip the unnecessary
284 * lookup_entry, this fixes the corner case with
285 * completely empty filesystems where lookup_entry correctly
286 * returning -1 is incorrectly treated as an error
287 */
288 return dir;
289
290 start = sBlk.s.directory_table_start + (*i)->start;
291 bytes = lookup_entry(directory_table_hash, start);
292
293 if(bytes == -1)
294 EXIT_UNSQUASH("squashfs_opendir: directory block %d not "
295 "found!\n", block_start);
296
297 bytes += (*i)->offset;
298 size = (*i)->data + bytes - 3;
299
300 while(bytes < size) {
301 SQUASHFS_SWAP_DIR_HEADER(directory_table + bytes, &dirh);
302
303 dir_count = dirh.count + 1;
304 TRACE("squashfs_opendir: Read directory header @ byte position "
305 "%d, %d directory entries\n", bytes, dir_count);
306 bytes += sizeof(dirh);
307
308 /* dir_count should never be larger than 256 */
309 if(dir_count > 256)
310 goto corrupted;
311
312 while(dir_count--) {
313 SQUASHFS_SWAP_DIR_ENTRY(directory_table + bytes, dire);
314
315 bytes += sizeof(*dire);
316
317 /* size should never be larger than SQUASHFS_NAME_LEN */
318 if(dire->size > SQUASHFS_NAME_LEN)
319 goto corrupted;
320
321 memcpy(dire->name, directory_table + bytes,
322 dire->size + 1);
323 dire->name[dire->size + 1] = '\0';
324 TRACE("squashfs_opendir: directory entry %s, inode "
325 "%d:%d, type %d\n", dire->name,
326 dirh.start_block, dire->offset, dire->type);
327 if((dir->dir_count % DIR_ENT_SIZE) == 0) {
328 new_dir = realloc(dir->dirs, (dir->dir_count +
329 DIR_ENT_SIZE) * sizeof(struct dir_ent));
330 if(new_dir == NULL)
331 EXIT_UNSQUASH("squashfs_opendir: "
332 "realloc failed!\n");
333 dir->dirs = new_dir;
334 }
335 strcpy(dir->dirs[dir->dir_count].name, dire->name);
336 dir->dirs[dir->dir_count].start_block =
337 dirh.start_block;
338 dir->dirs[dir->dir_count].offset = dire->offset;
339 dir->dirs[dir->dir_count].type = dire->type;
340 dir->dir_count ++;
341 bytes += dire->size + 1;
342 }
343 }
344
345 return dir;
346
347 corrupted:
348 free(dir->dirs);
349 free(dir);
350 return NULL;
351 }
352
353
read_uids_guids_4()354 int read_uids_guids_4()
355 {
356 int res, i;
357 int bytes = SQUASHFS_ID_BYTES(sBlk.s.no_ids);
358 int indexes = SQUASHFS_ID_BLOCKS(sBlk.s.no_ids);
359 long long id_index_table[indexes];
360
361 TRACE("read_uids_guids: no_ids %d\n", sBlk.s.no_ids);
362
363 id_table = malloc(bytes);
364 if(id_table == NULL) {
365 ERROR("read_uids_guids: failed to allocate id table\n");
366 return FALSE;
367 }
368
369 res = read_fs_bytes(fd, sBlk.s.id_table_start,
370 SQUASHFS_ID_BLOCK_BYTES(sBlk.s.no_ids), id_index_table);
371 if(res == FALSE) {
372 ERROR("read_uids_guids: failed to read id index table\n");
373 return FALSE;
374 }
375 SQUASHFS_INSWAP_ID_BLOCKS(id_index_table, indexes);
376
377 for(i = 0; i < indexes; i++) {
378 int expected = (i + 1) != indexes ? SQUASHFS_METADATA_SIZE :
379 bytes & (SQUASHFS_METADATA_SIZE - 1);
380 res = read_block(fd, id_index_table[i], NULL, expected,
381 ((char *) id_table) + i * SQUASHFS_METADATA_SIZE);
382 if(res == FALSE) {
383 ERROR("read_uids_guids: failed to read id table block"
384 "\n");
385 return FALSE;
386 }
387 }
388
389 SQUASHFS_INSWAP_INTS(id_table, sBlk.s.no_ids);
390
391 return TRUE;
392 }
393