Home | History | Annotate | Download | only in squashfs-tools
      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 (at) 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 
     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 
    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 
    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 
    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