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