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