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-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 
     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 
     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 
     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 
    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 
    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