unsquash-3.c revision cc8c394881f08d3a0a7c080f1be17a226136ce4f
1/*
2 * Unsquash a squashfs filesystem.  This is a highly compressed read only
3 * filesystem.
4 *
5 * Copyright (c) 2009, 2010
6 * Phillip Lougher <phillip@lougher.demon.co.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-3.c
23 */
24
25#include "unsquashfs.h"
26#include "squashfs_compat.h"
27
28static squashfs_fragment_entry_3 *fragment_table;
29
30int read_fragment_table_3()
31{
32	int res, i, indexes = SQUASHFS_FRAGMENT_INDEXES_3(sBlk.s.fragments);
33	squashfs_fragment_index fragment_table_index[indexes];
34
35	TRACE("read_fragment_table: %d fragments, reading %d fragment indexes "
36		"from 0x%llx\n", sBlk.s.fragments, indexes,
37		sBlk.s.fragment_table_start);
38
39	if(sBlk.s.fragments == 0)
40		return TRUE;
41
42	fragment_table = malloc(sBlk.s.fragments *
43		sizeof(squashfs_fragment_entry_3));
44	if(fragment_table == NULL)
45		EXIT_UNSQUASH("read_fragment_table: failed to allocate "
46			"fragment table\n");
47
48	if(swap) {
49		squashfs_fragment_index sfragment_table_index[indexes];
50
51		res = read_fs_bytes(fd, sBlk.s.fragment_table_start,
52			SQUASHFS_FRAGMENT_INDEX_BYTES_3(sBlk.s.fragments),
53			sfragment_table_index);
54		if(res == FALSE) {
55			ERROR("read_fragment_table: failed to read fragment "
56				"table index\n");
57			return FALSE;
58		}
59		SQUASHFS_SWAP_FRAGMENT_INDEXES_3(fragment_table_index,
60			sfragment_table_index, indexes);
61	} else {
62		res = read_fs_bytes(fd, sBlk.s.fragment_table_start,
63			SQUASHFS_FRAGMENT_INDEX_BYTES_3(sBlk.s.fragments),
64			fragment_table_index);
65		if(res == FALSE) {
66			ERROR("read_fragment_table: failed to read fragment "
67				"table index\n");
68			return FALSE;
69		}
70	}
71
72	for(i = 0; i < indexes; i++) {
73		int length = read_block(fd, fragment_table_index[i], NULL,
74			((char *) fragment_table) + (i *
75			SQUASHFS_METADATA_SIZE));
76		TRACE("Read fragment table block %d, from 0x%llx, length %d\n",
77			i, fragment_table_index[i], length);
78		if(length == FALSE) {
79			ERROR("read_fragment_table: failed to read fragment "
80				"table block\n");
81			return FALSE;
82		}
83	}
84
85	if(swap) {
86		squashfs_fragment_entry_3 sfragment;
87		for(i = 0; i < sBlk.s.fragments; i++) {
88			SQUASHFS_SWAP_FRAGMENT_ENTRY_3((&sfragment),
89				(&fragment_table[i]));
90			memcpy((char *) &fragment_table[i], (char *) &sfragment,
91				sizeof(squashfs_fragment_entry_3));
92		}
93	}
94
95	return TRUE;
96}
97
98
99void read_fragment_3(unsigned int fragment, long long *start_block, int *size)
100{
101	TRACE("read_fragment: reading fragment %d\n", fragment);
102
103	squashfs_fragment_entry_3 *fragment_entry = &fragment_table[fragment];
104	*start_block = fragment_entry->start_block;
105	*size = fragment_entry->size;
106}
107
108
109struct inode *read_inode_3(unsigned int start_block, unsigned int offset)
110{
111	static squashfs_inode_header_3 header;
112	long long start = sBlk.s.inode_table_start + start_block;
113	int bytes = lookup_entry(inode_table_hash, start);
114	char *block_ptr = inode_table + bytes + offset;
115	static struct inode i;
116
117	TRACE("read_inode: reading inode [%d:%d]\n", start_block,  offset);
118
119	if(bytes == -1)
120		EXIT_UNSQUASH("read_inode: inode table block %lld not found\n",
121			start);
122
123	if(swap) {
124		squashfs_base_inode_header_3 sinode;
125		memcpy(&sinode, block_ptr, sizeof(header.base));
126		SQUASHFS_SWAP_BASE_INODE_HEADER_3(&header.base, &sinode,
127			sizeof(squashfs_base_inode_header_3));
128	} else
129		memcpy(&header.base, block_ptr, sizeof(header.base));
130
131	i.xattr = SQUASHFS_INVALID_XATTR;
132	i.uid = (uid_t) uid_table[header.base.uid];
133	i.gid = header.base.guid == SQUASHFS_GUIDS ? i.uid :
134		(uid_t) guid_table[header.base.guid];
135	i.mode = lookup_type[header.base.inode_type] | header.base.mode;
136	i.type = header.base.inode_type;
137	i.time = header.base.mtime;
138	i.inode_number = header.base.inode_number;
139
140	switch(header.base.inode_type) {
141		case SQUASHFS_DIR_TYPE: {
142			squashfs_dir_inode_header_3 *inode = &header.dir;
143
144			if(swap) {
145				squashfs_dir_inode_header_3 sinode;
146				memcpy(&sinode, block_ptr, sizeof(header.dir));
147				SQUASHFS_SWAP_DIR_INODE_HEADER_3(&header.dir,
148					&sinode);
149			} else
150				memcpy(&header.dir, block_ptr,
151					sizeof(header.dir));
152
153			i.data = inode->file_size;
154			i.offset = inode->offset;
155			i.start = inode->start_block;
156			break;
157		}
158		case SQUASHFS_LDIR_TYPE: {
159			squashfs_ldir_inode_header_3 *inode = &header.ldir;
160
161			if(swap) {
162				squashfs_ldir_inode_header_3 sinode;
163				memcpy(&sinode, block_ptr, sizeof(header.ldir));
164				SQUASHFS_SWAP_LDIR_INODE_HEADER_3(&header.ldir,
165					&sinode);
166			} else
167				memcpy(&header.ldir, block_ptr,
168					sizeof(header.ldir));
169
170			i.data = inode->file_size;
171			i.offset = inode->offset;
172			i.start = inode->start_block;
173			break;
174		}
175		case SQUASHFS_FILE_TYPE: {
176			squashfs_reg_inode_header_3 *inode = &header.reg;
177
178			if(swap) {
179				squashfs_reg_inode_header_3 sinode;
180				memcpy(&sinode, block_ptr, sizeof(sinode));
181				SQUASHFS_SWAP_REG_INODE_HEADER_3(inode,
182					&sinode);
183			} else
184				memcpy(inode, block_ptr, sizeof(*inode));
185
186			i.data = inode->file_size;
187			i.frag_bytes = inode->fragment == SQUASHFS_INVALID_FRAG
188				?  0 : inode->file_size % sBlk.s.block_size;
189			i.fragment = inode->fragment;
190			i.offset = inode->offset;
191			i.blocks = inode->fragment == SQUASHFS_INVALID_FRAG ?
192				(i.data + sBlk.s.block_size - 1) >>
193				sBlk.s.block_log :
194				i.data >> sBlk.s.block_log;
195			i.start = inode->start_block;
196			i.sparse = 1;
197			i.block_ptr = block_ptr + sizeof(*inode);
198			break;
199		}
200		case SQUASHFS_LREG_TYPE: {
201			squashfs_lreg_inode_header_3 *inode = &header.lreg;
202
203			if(swap) {
204				squashfs_lreg_inode_header_3 sinode;
205				memcpy(&sinode, block_ptr, sizeof(sinode));
206				SQUASHFS_SWAP_LREG_INODE_HEADER_3(inode,
207					&sinode);
208			} else
209				memcpy(inode, block_ptr, sizeof(*inode));
210
211			i.data = inode->file_size;
212			i.frag_bytes = inode->fragment == SQUASHFS_INVALID_FRAG
213				?  0 : inode->file_size % sBlk.s.block_size;
214			i.fragment = inode->fragment;
215			i.offset = inode->offset;
216			i.blocks = inode->fragment == SQUASHFS_INVALID_FRAG ?
217				(inode->file_size + sBlk.s.block_size - 1) >>
218				sBlk.s.block_log :
219				inode->file_size >> sBlk.s.block_log;
220			i.start = inode->start_block;
221			i.sparse = 1;
222			i.block_ptr = block_ptr + sizeof(*inode);
223			break;
224		}
225		case SQUASHFS_SYMLINK_TYPE: {
226			squashfs_symlink_inode_header_3 *inodep =
227				&header.symlink;
228
229			if(swap) {
230				squashfs_symlink_inode_header_3 sinodep;
231				memcpy(&sinodep, block_ptr, sizeof(sinodep));
232				SQUASHFS_SWAP_SYMLINK_INODE_HEADER_3(inodep,
233					&sinodep);
234			} else
235				memcpy(inodep, block_ptr, sizeof(*inodep));
236
237			i.symlink = malloc(inodep->symlink_size + 1);
238			if(i.symlink == NULL)
239				EXIT_UNSQUASH("read_inode: failed to malloc "
240					"symlink data\n");
241			strncpy(i.symlink, block_ptr +
242				sizeof(squashfs_symlink_inode_header_3),
243				inodep->symlink_size);
244			i.symlink[inodep->symlink_size] = '\0';
245			i.data = inodep->symlink_size;
246			break;
247		}
248 		case SQUASHFS_BLKDEV_TYPE:
249	 	case SQUASHFS_CHRDEV_TYPE: {
250			squashfs_dev_inode_header_3 *inodep = &header.dev;
251
252			if(swap) {
253				squashfs_dev_inode_header_3 sinodep;
254				memcpy(&sinodep, block_ptr, sizeof(sinodep));
255				SQUASHFS_SWAP_DEV_INODE_HEADER_3(inodep,
256					&sinodep);
257			} else
258				memcpy(inodep, block_ptr, sizeof(*inodep));
259
260			i.data = inodep->rdev;
261			break;
262			}
263		case SQUASHFS_FIFO_TYPE:
264		case SQUASHFS_SOCKET_TYPE:
265			i.data = 0;
266			break;
267		default:
268			EXIT_UNSQUASH("Unknown inode type %d in read_inode!\n",
269				header.base.inode_type);
270	}
271	return &i;
272}
273
274
275struct dir *squashfs_opendir_3(unsigned int block_start, unsigned int offset,
276	struct inode **i)
277{
278	squashfs_dir_header_3 dirh;
279	char buffer[sizeof(squashfs_dir_entry_3) + SQUASHFS_NAME_LEN + 1]
280		__attribute__((aligned));
281	squashfs_dir_entry_3 *dire = (squashfs_dir_entry_3 *) buffer;
282	long long start;
283	int bytes;
284	int dir_count, size;
285	struct dir_ent *new_dir;
286	struct dir *dir;
287
288	TRACE("squashfs_opendir: inode start block %d, offset %d\n",
289		block_start, offset);
290
291	*i = s_ops.read_inode(block_start, offset);
292	start = sBlk.s.directory_table_start + (*i)->start;
293	bytes = lookup_entry(directory_table_hash, start);
294
295	if(bytes == -1)
296		EXIT_UNSQUASH("squashfs_opendir: directory block %d not "
297			"found!\n", block_start);
298
299	bytes += (*i)->offset;
300	size = (*i)->data + bytes - 3;
301
302	dir = malloc(sizeof(struct dir));
303	if(dir == NULL)
304		EXIT_UNSQUASH("squashfs_opendir: malloc failed!\n");
305
306	dir->dir_count = 0;
307	dir->cur_entry = 0;
308	dir->mode = (*i)->mode;
309	dir->uid = (*i)->uid;
310	dir->guid = (*i)->gid;
311	dir->mtime = (*i)->time;
312	dir->xattr = (*i)->xattr;
313	dir->dirs = NULL;
314
315	while(bytes < size) {
316		if(swap) {
317			squashfs_dir_header_3 sdirh;
318			memcpy(&sdirh, directory_table + bytes, sizeof(sdirh));
319			SQUASHFS_SWAP_DIR_HEADER_3(&dirh, &sdirh);
320		} else
321			memcpy(&dirh, directory_table + bytes, sizeof(dirh));
322
323		dir_count = dirh.count + 1;
324		TRACE("squashfs_opendir: Read directory header @ byte position "
325			"%d, %d directory entries\n", bytes, dir_count);
326		bytes += sizeof(dirh);
327
328		while(dir_count--) {
329			if(swap) {
330				squashfs_dir_entry_3 sdire;
331				memcpy(&sdire, directory_table + bytes,
332					sizeof(sdire));
333				SQUASHFS_SWAP_DIR_ENTRY_3(dire, &sdire);
334			} else
335				memcpy(dire, directory_table + bytes,
336					sizeof(*dire));
337			bytes += sizeof(*dire);
338
339			memcpy(dire->name, directory_table + bytes,
340				dire->size + 1);
341			dire->name[dire->size + 1] = '\0';
342			TRACE("squashfs_opendir: directory entry %s, inode "
343				"%d:%d, type %d\n", dire->name,
344				dirh.start_block, dire->offset, dire->type);
345			if((dir->dir_count % DIR_ENT_SIZE) == 0) {
346				new_dir = realloc(dir->dirs, (dir->dir_count +
347					DIR_ENT_SIZE) * sizeof(struct dir_ent));
348				if(new_dir == NULL)
349					EXIT_UNSQUASH("squashfs_opendir: "
350						"realloc failed!\n");
351				dir->dirs = new_dir;
352			}
353			strcpy(dir->dirs[dir->dir_count].name, dire->name);
354			dir->dirs[dir->dir_count].start_block =
355				dirh.start_block;
356			dir->dirs[dir->dir_count].offset = dire->offset;
357			dir->dirs[dir->dir_count].type = dire->type;
358			dir->dir_count ++;
359			bytes += dire->size + 1;
360		}
361	}
362
363	return dir;
364}
365