1 /* 2 * Unsquash a squashfs filesystem. This is a highly compressed read only 3 * filesystem. 4 * 5 * Copyright (c) 2009, 2010, 2011, 2012 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-1.c 23 */ 24 25 #include "unsquashfs.h" 26 #include "squashfs_compat.h" 27 28 void read_block_list_1(unsigned int *block_list, char *block_ptr, int blocks) 29 { 30 unsigned short block_size; 31 int i; 32 33 TRACE("read_block_list: blocks %d\n", blocks); 34 35 for(i = 0; i < blocks; i++, block_ptr += 2) { 36 if(swap) { 37 unsigned short sblock_size; 38 memcpy(&sblock_size, block_ptr, sizeof(unsigned short)); 39 SQUASHFS_SWAP_SHORTS_3((&block_size), &sblock_size, 1); 40 } else 41 memcpy(&block_size, block_ptr, sizeof(unsigned short)); 42 block_list[i] = SQUASHFS_COMPRESSED_SIZE(block_size) | 43 (SQUASHFS_COMPRESSED(block_size) ? 0 : 44 SQUASHFS_COMPRESSED_BIT_BLOCK); 45 } 46 } 47 48 49 int read_fragment_table_1(long long *directory_table_end) 50 { 51 TRACE("read_fragment_table\n"); 52 *directory_table_end = sBlk.s.fragment_table_start; 53 return TRUE; 54 } 55 56 57 struct inode *read_inode_1(unsigned int start_block, unsigned int offset) 58 { 59 static union squashfs_inode_header_1 header; 60 long long start = sBlk.s.inode_table_start + start_block; 61 int bytes = lookup_entry(inode_table_hash, start); 62 char *block_ptr = inode_table + bytes + offset; 63 static struct inode i; 64 65 TRACE("read_inode: reading inode [%d:%d]\n", start_block, offset); 66 67 if(bytes == -1) 68 EXIT_UNSQUASH("read_inode: inode table block %lld not found\n", 69 start); 70 71 if(swap) { 72 squashfs_base_inode_header_1 sinode; 73 memcpy(&sinode, block_ptr, sizeof(header.base)); 74 SQUASHFS_SWAP_BASE_INODE_HEADER_1(&header.base, &sinode, 75 sizeof(squashfs_base_inode_header_1)); 76 } else 77 memcpy(&header.base, block_ptr, sizeof(header.base)); 78 79 i.uid = (uid_t) uid_table[(header.base.inode_type - 1) / 80 SQUASHFS_TYPES * 16 + header.base.uid]; 81 if(header.base.inode_type == SQUASHFS_IPC_TYPE) { 82 squashfs_ipc_inode_header_1 *inodep = &header.ipc; 83 84 if(swap) { 85 squashfs_ipc_inode_header_1 sinodep; 86 memcpy(&sinodep, block_ptr, sizeof(sinodep)); 87 SQUASHFS_SWAP_IPC_INODE_HEADER_1(inodep, &sinodep); 88 } else 89 memcpy(inodep, block_ptr, sizeof(*inodep)); 90 91 if(inodep->type == SQUASHFS_SOCKET_TYPE) { 92 i.mode = S_IFSOCK | header.base.mode; 93 i.type = SQUASHFS_SOCKET_TYPE; 94 } else { 95 i.mode = S_IFIFO | header.base.mode; 96 i.type = SQUASHFS_FIFO_TYPE; 97 } 98 i.uid = (uid_t) uid_table[inodep->offset * 16 + inodep->uid]; 99 } else { 100 i.mode = lookup_type[(header.base.inode_type - 1) % 101 SQUASHFS_TYPES + 1] | header.base.mode; 102 i.type = (header.base.inode_type - 1) % SQUASHFS_TYPES + 1; 103 } 104 105 i.xattr = SQUASHFS_INVALID_XATTR; 106 i.gid = header.base.guid == 15 ? i.uid : 107 (uid_t) guid_table[header.base.guid]; 108 i.time = sBlk.s.mkfs_time; 109 i.inode_number = inode_number ++; 110 111 switch(i.type) { 112 case SQUASHFS_DIR_TYPE: { 113 squashfs_dir_inode_header_1 *inode = &header.dir; 114 115 if(swap) { 116 squashfs_dir_inode_header_1 sinode; 117 memcpy(&sinode, block_ptr, sizeof(header.dir)); 118 SQUASHFS_SWAP_DIR_INODE_HEADER_1(inode, 119 &sinode); 120 } else 121 memcpy(inode, block_ptr, sizeof(header.dir)); 122 123 i.data = inode->file_size; 124 i.offset = inode->offset; 125 i.start = inode->start_block; 126 i.time = inode->mtime; 127 break; 128 } 129 case SQUASHFS_FILE_TYPE: { 130 squashfs_reg_inode_header_1 *inode = &header.reg; 131 132 if(swap) { 133 squashfs_reg_inode_header_1 sinode; 134 memcpy(&sinode, block_ptr, sizeof(sinode)); 135 SQUASHFS_SWAP_REG_INODE_HEADER_1(inode, 136 &sinode); 137 } else 138 memcpy(inode, block_ptr, sizeof(*inode)); 139 140 i.data = inode->file_size; 141 i.time = inode->mtime; 142 i.blocks = (i.data + sBlk.s.block_size - 1) >> 143 sBlk.s.block_log; 144 i.start = inode->start_block; 145 i.block_ptr = block_ptr + sizeof(*inode); 146 i.fragment = 0; 147 i.frag_bytes = 0; 148 i.offset = 0; 149 i.sparse = 0; 150 break; 151 } 152 case SQUASHFS_SYMLINK_TYPE: { 153 squashfs_symlink_inode_header_1 *inodep = 154 &header.symlink; 155 156 if(swap) { 157 squashfs_symlink_inode_header_1 sinodep; 158 memcpy(&sinodep, block_ptr, sizeof(sinodep)); 159 SQUASHFS_SWAP_SYMLINK_INODE_HEADER_1(inodep, 160 &sinodep); 161 } else 162 memcpy(inodep, block_ptr, sizeof(*inodep)); 163 164 i.symlink = malloc(inodep->symlink_size + 1); 165 if(i.symlink == NULL) 166 EXIT_UNSQUASH("read_inode: failed to malloc " 167 "symlink data\n"); 168 strncpy(i.symlink, block_ptr + 169 sizeof(squashfs_symlink_inode_header_1), 170 inodep->symlink_size); 171 i.symlink[inodep->symlink_size] = '\0'; 172 i.data = inodep->symlink_size; 173 break; 174 } 175 case SQUASHFS_BLKDEV_TYPE: 176 case SQUASHFS_CHRDEV_TYPE: { 177 squashfs_dev_inode_header_1 *inodep = &header.dev; 178 179 if(swap) { 180 squashfs_dev_inode_header_1 sinodep; 181 memcpy(&sinodep, block_ptr, sizeof(sinodep)); 182 SQUASHFS_SWAP_DEV_INODE_HEADER_1(inodep, 183 &sinodep); 184 } else 185 memcpy(inodep, block_ptr, sizeof(*inodep)); 186 187 i.data = inodep->rdev; 188 break; 189 } 190 case SQUASHFS_FIFO_TYPE: 191 case SQUASHFS_SOCKET_TYPE: { 192 i.data = 0; 193 break; 194 } 195 default: 196 EXIT_UNSQUASH("Unknown inode type %d in " 197 " read_inode_header_1!\n", 198 header.base.inode_type); 199 } 200 return &i; 201 } 202 203 204 struct dir *squashfs_opendir_1(unsigned int block_start, unsigned int offset, 205 struct inode **i) 206 { 207 squashfs_dir_header_2 dirh; 208 char buffer[sizeof(squashfs_dir_entry_2) + SQUASHFS_NAME_LEN + 1] 209 __attribute__((aligned)); 210 squashfs_dir_entry_2 *dire = (squashfs_dir_entry_2 *) buffer; 211 long long start; 212 int bytes; 213 int dir_count, size; 214 struct dir_ent *new_dir; 215 struct dir *dir; 216 217 TRACE("squashfs_opendir: inode start block %d, offset %d\n", 218 block_start, offset); 219 220 *i = s_ops.read_inode(block_start, offset); 221 222 dir = malloc(sizeof(struct dir)); 223 if(dir == NULL) 224 EXIT_UNSQUASH("squashfs_opendir: malloc failed!\n"); 225 226 dir->dir_count = 0; 227 dir->cur_entry = 0; 228 dir->mode = (*i)->mode; 229 dir->uid = (*i)->uid; 230 dir->guid = (*i)->gid; 231 dir->mtime = (*i)->time; 232 dir->xattr = (*i)->xattr; 233 dir->dirs = NULL; 234 235 if ((*i)->data == 0) 236 /* 237 * if the directory is empty, skip the unnecessary 238 * lookup_entry, this fixes the corner case with 239 * completely empty filesystems where lookup_entry correctly 240 * returning -1 is incorrectly treated as an error 241 */ 242 return dir; 243 244 start = sBlk.s.directory_table_start + (*i)->start; 245 bytes = lookup_entry(directory_table_hash, start); 246 if(bytes == -1) 247 EXIT_UNSQUASH("squashfs_opendir: directory block %d not " 248 "found!\n", block_start); 249 250 bytes += (*i)->offset; 251 size = (*i)->data + bytes; 252 253 while(bytes < size) { 254 if(swap) { 255 squashfs_dir_header_2 sdirh; 256 memcpy(&sdirh, directory_table + bytes, sizeof(sdirh)); 257 SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh); 258 } else 259 memcpy(&dirh, directory_table + bytes, sizeof(dirh)); 260 261 dir_count = dirh.count + 1; 262 TRACE("squashfs_opendir: Read directory header @ byte position " 263 "%d, %d directory entries\n", bytes, dir_count); 264 bytes += sizeof(dirh); 265 266 /* dir_count should never be larger than 256 */ 267 if(dir_count > 256) 268 goto corrupted; 269 270 while(dir_count--) { 271 if(swap) { 272 squashfs_dir_entry_2 sdire; 273 memcpy(&sdire, directory_table + bytes, 274 sizeof(sdire)); 275 SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire); 276 } else 277 memcpy(dire, directory_table + bytes, 278 sizeof(*dire)); 279 bytes += sizeof(*dire); 280 281 /* size should never be larger than SQUASHFS_NAME_LEN */ 282 if(dire->size > SQUASHFS_NAME_LEN) 283 goto corrupted; 284 285 memcpy(dire->name, directory_table + bytes, 286 dire->size + 1); 287 dire->name[dire->size + 1] = '\0'; 288 TRACE("squashfs_opendir: directory entry %s, inode " 289 "%d:%d, type %d\n", dire->name, 290 dirh.start_block, dire->offset, dire->type); 291 if((dir->dir_count % DIR_ENT_SIZE) == 0) { 292 new_dir = realloc(dir->dirs, (dir->dir_count + 293 DIR_ENT_SIZE) * sizeof(struct dir_ent)); 294 if(new_dir == NULL) 295 EXIT_UNSQUASH("squashfs_opendir: " 296 "realloc failed!\n"); 297 dir->dirs = new_dir; 298 } 299 strcpy(dir->dirs[dir->dir_count].name, dire->name); 300 dir->dirs[dir->dir_count].start_block = 301 dirh.start_block; 302 dir->dirs[dir->dir_count].offset = dire->offset; 303 dir->dirs[dir->dir_count].type = dire->type; 304 dir->dir_count ++; 305 bytes += dire->size + 1; 306 } 307 } 308 309 return dir; 310 311 corrupted: 312 free(dir->dirs); 313 free(dir); 314 return NULL; 315 } 316 317 318 int read_uids_guids_1() 319 { 320 int res; 321 322 TRACE("read_uids_guids: no_uids %d, no_guids %d\n", sBlk.no_uids, 323 sBlk.no_guids); 324 325 uid_table = malloc((sBlk.no_uids + sBlk.no_guids) * 326 sizeof(unsigned int)); 327 if(uid_table == NULL) { 328 ERROR("read_uids_guids: failed to allocate uid/gid table\n"); 329 return FALSE; 330 } 331 332 guid_table = uid_table + sBlk.no_uids; 333 334 if(swap) { 335 unsigned int suid_table[sBlk.no_uids + sBlk.no_guids]; 336 337 res = read_fs_bytes(fd, sBlk.uid_start, (sBlk.no_uids + 338 sBlk.no_guids) * sizeof(unsigned int), suid_table); 339 if(res == FALSE) { 340 ERROR("read_uids_guids: failed to read uid/gid table" 341 "\n"); 342 return FALSE; 343 } 344 SQUASHFS_SWAP_INTS_3(uid_table, suid_table, 345 sBlk.no_uids + sBlk.no_guids); 346 } else { 347 res = read_fs_bytes(fd, sBlk.uid_start, (sBlk.no_uids + 348 sBlk.no_guids) * sizeof(unsigned int), uid_table); 349 if(res == FALSE) { 350 ERROR("read_uids_guids: failed to read uid/gid table" 351 "\n"); 352 return FALSE; 353 } 354 } 355 356 return TRUE; 357 } 358