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