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