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