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-2.c
23 */
24
25#include "unsquashfs.h"
26#include "squashfs_compat.h"
27
28static squashfs_fragment_entry_2 *fragment_table;
29
30void read_block_list_2(unsigned int *block_list, char *block_ptr, int blocks)
31{
32	TRACE("read_block_list: blocks %d\n", blocks);
33
34	if(swap) {
35		unsigned int sblock_list[blocks];
36		memcpy(sblock_list, block_ptr, blocks * sizeof(unsigned int));
37		SQUASHFS_SWAP_INTS_3(block_list, sblock_list, blocks);
38	} else
39		memcpy(block_list, block_ptr, blocks * sizeof(unsigned int));
40}
41
42
43int read_fragment_table_2()
44{
45	int res, i, indexes = SQUASHFS_FRAGMENT_INDEXES_2(sBlk.s.fragments);
46	unsigned int fragment_table_index[indexes];
47
48	TRACE("read_fragment_table: %d fragments, reading %d fragment indexes "
49		"from 0x%llx\n", sBlk.s.fragments, indexes,
50		sBlk.s.fragment_table_start);
51
52	if(sBlk.s.fragments == 0)
53		return TRUE;
54
55	fragment_table = malloc(sBlk.s.fragments *
56		sizeof(squashfs_fragment_entry_2));
57	if(fragment_table == NULL)
58		EXIT_UNSQUASH("read_fragment_table: failed to allocate "
59			"fragment table\n");
60
61	if(swap) {
62		 unsigned int sfragment_table_index[indexes];
63
64		 res = read_fs_bytes(fd, sBlk.s.fragment_table_start,
65			SQUASHFS_FRAGMENT_INDEX_BYTES_2(sBlk.s.fragments),
66			sfragment_table_index);
67		if(res == FALSE) {
68			ERROR("read_fragment_table: failed to read fragment "
69				"table index\n");
70			return FALSE;
71		}
72		SQUASHFS_SWAP_FRAGMENT_INDEXES_2(fragment_table_index,
73			sfragment_table_index, indexes);
74	} else {
75		res = read_fs_bytes(fd, sBlk.s.fragment_table_start,
76			SQUASHFS_FRAGMENT_INDEX_BYTES_2(sBlk.s.fragments),
77			fragment_table_index);
78		if(res == FALSE) {
79			ERROR("read_fragment_table: failed to read fragment "
80				"table index\n");
81			return FALSE;
82		}
83	}
84
85	for(i = 0; i < indexes; i++) {
86		int length = read_block(fd, fragment_table_index[i], NULL,
87			((char *) fragment_table) + (i *
88			SQUASHFS_METADATA_SIZE));
89		TRACE("Read fragment table block %d, from 0x%x, length %d\n", i,
90			fragment_table_index[i], length);
91		if(length == FALSE) {
92			ERROR("read_fragment_table: failed to read fragment "
93				"table block\n");
94			return FALSE;
95		}
96	}
97
98	if(swap) {
99		squashfs_fragment_entry_2 sfragment;
100		for(i = 0; i < sBlk.s.fragments; i++) {
101			SQUASHFS_SWAP_FRAGMENT_ENTRY_2((&sfragment),
102				(&fragment_table[i]));
103			memcpy((char *) &fragment_table[i], (char *) &sfragment,
104				sizeof(squashfs_fragment_entry_2));
105		}
106	}
107
108	return TRUE;
109}
110
111
112void read_fragment_2(unsigned int fragment, long long *start_block, int *size)
113{
114	TRACE("read_fragment: reading fragment %d\n", fragment);
115
116	squashfs_fragment_entry_2 *fragment_entry = &fragment_table[fragment];
117	*start_block = fragment_entry->start_block;
118	*size = fragment_entry->size;
119}
120
121
122struct inode *read_inode_2(unsigned int start_block, unsigned int offset)
123{
124	static union squashfs_inode_header_2 header;
125	long long start = sBlk.s.inode_table_start + start_block;
126	int bytes = lookup_entry(inode_table_hash, start);
127	char *block_ptr = inode_table + bytes + offset;
128	static struct inode i;
129
130	TRACE("read_inode: reading inode [%d:%d]\n", start_block,  offset);
131
132	if(bytes == -1)
133		EXIT_UNSQUASH("read_inode: inode table block %lld not found\n",
134			start);
135
136	if(swap) {
137		squashfs_base_inode_header_2 sinode;
138		memcpy(&sinode, block_ptr, sizeof(header.base));
139		SQUASHFS_SWAP_BASE_INODE_HEADER_2(&header.base, &sinode,
140			sizeof(squashfs_base_inode_header_2));
141	} else
142		memcpy(&header.base, block_ptr, sizeof(header.base));
143
144	i.xattr = SQUASHFS_INVALID_XATTR;
145	i.uid = (uid_t) uid_table[header.base.uid];
146	i.gid = header.base.guid == SQUASHFS_GUIDS ? i.uid :
147		(uid_t) guid_table[header.base.guid];
148	i.mode = lookup_type[header.base.inode_type] | header.base.mode;
149	i.type = header.base.inode_type;
150	i.time = sBlk.s.mkfs_time;
151	i.inode_number = inode_number++;
152
153	switch(header.base.inode_type) {
154		case SQUASHFS_DIR_TYPE: {
155			squashfs_dir_inode_header_2 *inode = &header.dir;
156
157			if(swap) {
158				squashfs_dir_inode_header_2 sinode;
159				memcpy(&sinode, block_ptr, sizeof(header.dir));
160				SQUASHFS_SWAP_DIR_INODE_HEADER_2(&header.dir,
161					&sinode);
162			} else
163				memcpy(&header.dir, block_ptr,
164					sizeof(header.dir));
165
166			i.data = inode->file_size;
167			i.offset = inode->offset;
168			i.start = inode->start_block;
169			i.time = inode->mtime;
170			break;
171		}
172		case SQUASHFS_LDIR_TYPE: {
173			squashfs_ldir_inode_header_2 *inode = &header.ldir;
174
175			if(swap) {
176				squashfs_ldir_inode_header_2 sinode;
177				memcpy(&sinode, block_ptr, sizeof(header.ldir));
178				SQUASHFS_SWAP_LDIR_INODE_HEADER_2(&header.ldir,
179					&sinode);
180			} else
181				memcpy(&header.ldir, block_ptr,
182					sizeof(header.ldir));
183
184			i.data = inode->file_size;
185			i.offset = inode->offset;
186			i.start = inode->start_block;
187			i.time = inode->mtime;
188			break;
189		}
190		case SQUASHFS_FILE_TYPE: {
191			squashfs_reg_inode_header_2 *inode = &header.reg;
192
193			if(swap) {
194				squashfs_reg_inode_header_2 sinode;
195				memcpy(&sinode, block_ptr, sizeof(sinode));
196				SQUASHFS_SWAP_REG_INODE_HEADER_2(inode,
197					&sinode);
198			} else
199				memcpy(inode, block_ptr, sizeof(*inode));
200
201			i.data = inode->file_size;
202			i.time = inode->mtime;
203			i.frag_bytes = inode->fragment == SQUASHFS_INVALID_FRAG
204				?  0 : inode->file_size % sBlk.s.block_size;
205			i.fragment = inode->fragment;
206			i.offset = inode->offset;
207			i.blocks = inode->fragment == SQUASHFS_INVALID_FRAG ?
208				(i.data + sBlk.s.block_size - 1) >>
209				sBlk.s.block_log : i.data >>
210				sBlk.s.block_log;
211			i.start = inode->start_block;
212			i.sparse = 0;
213			i.block_ptr = block_ptr + sizeof(*inode);
214			break;
215		}
216		case SQUASHFS_SYMLINK_TYPE: {
217			squashfs_symlink_inode_header_2 *inodep =
218				&header.symlink;
219
220			if(swap) {
221				squashfs_symlink_inode_header_2 sinodep;
222				memcpy(&sinodep, block_ptr, sizeof(sinodep));
223				SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(inodep,
224					&sinodep);
225			} else
226				memcpy(inodep, block_ptr, sizeof(*inodep));
227
228			i.symlink = malloc(inodep->symlink_size + 1);
229			if(i.symlink == NULL)
230				EXIT_UNSQUASH("read_inode: failed to malloc "
231					"symlink data\n");
232			strncpy(i.symlink, block_ptr +
233				sizeof(squashfs_symlink_inode_header_2),
234				inodep->symlink_size);
235			i.symlink[inodep->symlink_size] = '\0';
236			i.data = inodep->symlink_size;
237			break;
238		}
239 		case SQUASHFS_BLKDEV_TYPE:
240	 	case SQUASHFS_CHRDEV_TYPE: {
241			squashfs_dev_inode_header_2 *inodep = &header.dev;
242
243			if(swap) {
244				squashfs_dev_inode_header_2 sinodep;
245				memcpy(&sinodep, block_ptr, sizeof(sinodep));
246				SQUASHFS_SWAP_DEV_INODE_HEADER_2(inodep,
247					&sinodep);
248			} else
249				memcpy(inodep, block_ptr, sizeof(*inodep));
250
251			i.data = inodep->rdev;
252			break;
253			}
254		case SQUASHFS_FIFO_TYPE:
255		case SQUASHFS_SOCKET_TYPE:
256			i.data = 0;
257			break;
258		default:
259			EXIT_UNSQUASH("Unknown inode type %d in "
260				"read_inode_header_2!\n",
261				header.base.inode_type);
262	}
263	return &i;
264}
265