From 4aca87515a5083ae0e31ce3177189fd43b6d05ac Mon Sep 17 00:00:00 2001 From: Andreas Baumann Date: Sat, 3 Jan 2015 13:58:15 +0100 Subject: patch to Vanilla Tomato 1.28 --- .../src/linux/linux/scripts/squashfs/unsquashfs.c | 946 +++++++++++++++++++++ 1 file changed, 946 insertions(+) create mode 100644 release/src/linux/linux/scripts/squashfs/unsquashfs.c (limited to 'release/src/linux/linux/scripts/squashfs/unsquashfs.c') diff --git a/release/src/linux/linux/scripts/squashfs/unsquashfs.c b/release/src/linux/linux/scripts/squashfs/unsquashfs.c new file mode 100644 index 00000000..dae8fd62 --- /dev/null +++ b/release/src/linux/linux/scripts/squashfs/unsquashfs.c @@ -0,0 +1,946 @@ +/* + * Unsquash a squashfs filesystem. This is a highly compressed read only filesystem. + * + * Copyright (c) 2002, 2003, 2004, 2005, 2006 + * Phillip Lougher + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * unsquash.c + */ + +#define TRUE 1 +#define FALSE 0 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef linux +#define __BYTE_ORDER BYTE_ORDER +#define __BIG_ENDIAN BIG_ENDIAN +#define __LITTLE_ENDIAN LITTLE_ENDIAN +#else +#include +#endif + +#include +#include "read_fs.h" +#include "global.h" + +#include + +#ifdef SQUASHFS_TRACE +#define TRACE(s, args...) do { \ + printf("mksquashfs: "s, ## args); \ + } while(0) +#else +#define TRACE(s, args...) +#endif + +#define ERROR(s, args...) do { \ + fprintf(stderr, s, ## args); \ + } while(0) + +#define EXIT_UNSQUASH(s, args...) do { \ + fprintf(stderr, "FATAL ERROR aborting: "s, ## args); \ + } while(0) + +struct hash_table_entry { + int start; + int bytes; + struct hash_table_entry *next; +}; + +int bytes = 0, swap, file_count = 0, dir_count = 0, sym_count = 0, dev_count = 0, fifo_count = 0; +char *inode_table = NULL, *directory_table = NULL; +struct hash_table_entry *inode_table_hash[65536], *directory_table_hash[65536]; +int fd; +squashfs_fragment_entry *fragment_table; +unsigned int *uid_table, *guid_table; +unsigned int cached_frag = SQUASHFS_INVALID_FRAG; +char *fragment_data; +char *file_data; +char *data; +unsigned int block_size; +int lsonly = FALSE, info = FALSE; +char **created_inode; + +#define CALCULATE_HASH(start) (start & 0xffff) + +int add_entry(struct hash_table_entry *hash_table[], int start, int bytes) +{ + int hash = CALCULATE_HASH(start); + struct hash_table_entry *hash_table_entry; + + if((hash_table_entry = malloc(sizeof(struct hash_table_entry))) == NULL) { + ERROR("add_hash: out of memory in malloc\n"); + return FALSE; + } + + hash_table_entry->start = start; + hash_table_entry->bytes = bytes; + hash_table_entry->next = hash_table[hash]; + hash_table[hash] = hash_table_entry; + + return TRUE; +} + + +int lookup_entry(struct hash_table_entry *hash_table[], int start) +{ + int hash = CALCULATE_HASH(start); + struct hash_table_entry *hash_table_entry; + + for(hash_table_entry = hash_table[hash]; hash_table_entry; hash_table_entry = hash_table_entry->next) + if(hash_table_entry->start == start) + return hash_table_entry->bytes; + + return -1; +} + + +int read_bytes(long long byte, int bytes, char *buff) +{ + off_t off = byte; + + TRACE("read_bytes: reading from position 0x%llx, bytes %d\n", byte, bytes); + + if(lseek(fd, off, SEEK_SET) == -1) { + ERROR("Lseek failed because %s\b", strerror(errno)); + return FALSE; + } + + if(read(fd, buff, bytes) == -1) { + ERROR("Read on destination failed because %s\n", strerror(errno)); + return FALSE; + } + + return TRUE; +} + + +int read_block(long long start, long long *next, char *block, squashfs_super_block *sBlk) +{ + unsigned short c_byte; + int offset = 2; + + if(swap) { + if(read_bytes(start, 2, block) == FALSE) + goto failed; + ((unsigned char *) &c_byte)[1] = block[0]; + ((unsigned char *) &c_byte)[0] = block[1]; + } else + if(read_bytes(start, 2, (char *)&c_byte) == FALSE) + goto failed; + + TRACE("read_block: block @0x%llx, %d %s bytes\n", start, SQUASHFS_COMPRESSED_SIZE(c_byte), SQUASHFS_COMPRESSED(c_byte) ? "compressed" : "uncompressed"); + + if(SQUASHFS_CHECK_DATA(sBlk->flags)) + offset = 3; + if(SQUASHFS_COMPRESSED(c_byte)) { + char buffer[SQUASHFS_METADATA_SIZE]; + int res; + unsigned long bytes = SQUASHFS_METADATA_SIZE; + + c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte); + if(read_bytes(start + offset, c_byte, buffer) == FALSE) + goto failed; + + if((res = uncompress((unsigned char *) block, &bytes, (const unsigned char *) buffer, c_byte)) != Z_OK) { + if(res == Z_MEM_ERROR) + ERROR("zlib::uncompress failed, not enough memory\n"); + else if(res == Z_BUF_ERROR) + ERROR("zlib::uncompress failed, not enough room in output buffer\n"); + else + ERROR("zlib::uncompress failed, unknown error %d\n", res); + goto failed; + } + if(next) + *next = start + offset + c_byte; + return bytes; + } else { + c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte); + if(read_bytes(start + offset, c_byte, block) == FALSE) + goto failed; + if(next) + *next = start + offset + c_byte; + return c_byte; + } + +failed: + return FALSE; +} + + +int read_data_block(long long start, unsigned int size, char *block) +{ + int res; + unsigned long bytes = block_size; + int c_byte = SQUASHFS_COMPRESSED_SIZE_BLOCK(size); + + TRACE("read_data_block: block @0x%llx, %d %s bytes\n", start, SQUASHFS_COMPRESSED_SIZE_BLOCK(c_byte), SQUASHFS_COMPRESSED_BLOCK(c_byte) ? "compressed" : "uncompressed"); + + if(SQUASHFS_COMPRESSED_BLOCK(size)) { + if(read_bytes(start, c_byte, data) == FALSE) + return 0; + + if((res = uncompress((unsigned char *) block, &bytes, (const unsigned char *) data, c_byte)) != Z_OK) { + if(res == Z_MEM_ERROR) + ERROR("zlib::uncompress failed, not enough memory\n"); + else if(res == Z_BUF_ERROR) + ERROR("zlib::uncompress failed, not enough room in output buffer\n"); + else + ERROR("zlib::uncompress failed, unknown error %d\n", res); + return 0; + } + + return bytes; + } else { + if(read_bytes(start, c_byte, block) == FALSE) + return 0; + + return c_byte; + } +} + + +void uncompress_inode_table(long long start, long long end, squashfs_super_block *sBlk) +{ + int size = 0, bytes = 0, res; + + while(start < end) { + if((size - bytes < SQUASHFS_METADATA_SIZE) && + ((inode_table = realloc(inode_table, size += SQUASHFS_METADATA_SIZE)) == NULL)) + EXIT_UNSQUASH("uncompress_inode_table: out of memory in realloc\n"); + TRACE("uncompress_inode_table: reading block 0x%llx\n", start); + add_entry(inode_table_hash, start, bytes); + if((res = read_block(start, &start, inode_table + bytes, sBlk)) == 0) { + free(inode_table); + EXIT_UNSQUASH("uncompress_inode_table: failed to read block\n"); + } + bytes += res; + } +} + + +int set_attributes(char *pathname, unsigned int mode, unsigned int uid, unsigned int guid, +unsigned int mtime, unsigned int set_mode) +{ + struct utimbuf times = { (time_t) mtime, (time_t) mtime }; + + if(utime(pathname, ×) == -1) { + ERROR("set_attributes: failed to set time on %s, because %s\n", pathname, strerror(errno)); + return FALSE; + } + + if(set_mode && chmod(pathname, (mode_t) mode) == -1) { + ERROR("set_attributes: failed to change mode %s, because %s\n", pathname, strerror(errno)); + return FALSE; + } + + if(geteuid() == 0) { + uid_t uid_value = (uid_t) uid_table[uid]; + uid_t guid_value = guid == SQUASHFS_GUIDS ? uid_value : (uid_t) guid_table[guid]; + + if(chown(pathname, uid_value, guid_value) == -1) { + ERROR("set_attributes: failed to change uid and gids on %s, because %s\n", pathname, strerror(errno)); + return FALSE; + } + } + + return TRUE; +} + + +void read_uids_guids(squashfs_super_block *sBlk) +{ + if((uid_table = malloc((sBlk->no_uids + sBlk->no_guids) * sizeof(unsigned int))) == NULL) + EXIT_UNSQUASH("read_uids_guids: failed to allocate uid/gid table\n"); + + guid_table = uid_table + sBlk->no_uids; + + if(read_bytes(sBlk->uid_start, (sBlk->no_uids + sBlk->no_guids) * sizeof(unsigned int), (char *) uid_table) == + FALSE) + EXIT_UNSQUASH("read_uids_guids: failed to read uid/gid table\n"); +} + + +void read_fragment_table(squashfs_super_block *sBlk) +{ + int i, indexes = SQUASHFS_FRAGMENT_INDEXES(sBlk->fragments); + squashfs_fragment_index fragment_table_index[indexes]; + + TRACE("read_fragment_table: %d fragments, reading %d fragment indexes from 0x%llx\n", sBlk->fragments, indexes, sBlk->fragment_table_start); + if(sBlk->fragments == 0) + return; + + if((fragment_table = (squashfs_fragment_entry *) malloc(sBlk->fragments * sizeof(squashfs_fragment_entry))) == NULL) + EXIT_UNSQUASH("read_fragment_table: failed to allocate fragment table\n"); + + if(swap) { + squashfs_fragment_index sfragment_table_index[indexes]; + + read_bytes(sBlk->fragment_table_start, SQUASHFS_FRAGMENT_INDEX_BYTES(sBlk->fragments), (char *) sfragment_table_index); + SQUASHFS_SWAP_FRAGMENT_INDEXES(fragment_table_index, sfragment_table_index, indexes); + } else + read_bytes(sBlk->fragment_table_start, SQUASHFS_FRAGMENT_INDEX_BYTES(sBlk->fragments), (char *) fragment_table_index); + + for(i = 0; i < indexes; i++) { + int length = read_block(fragment_table_index[i], NULL, ((char *) fragment_table) + (i * SQUASHFS_METADATA_SIZE), sBlk); + TRACE("Read fragment table block %d, from 0x%llx, length %d\n", i, fragment_table_index[i], length); + } + + if(swap) { + squashfs_fragment_entry sfragment; + for(i = 0; i < sBlk->fragments; i++) { + SQUASHFS_SWAP_FRAGMENT_ENTRY((&sfragment), (&fragment_table[i])); + memcpy((char *) &fragment_table[i], (char *) &sfragment, sizeof(squashfs_fragment_entry)); + } + } +} + + +char *read_fragment(unsigned int fragment) +{ + TRACE("read_fragment: reading fragment %d\n", fragment); + + if(cached_frag == SQUASHFS_INVALID_FRAG || fragment != cached_frag) { + squashfs_fragment_entry *fragment_entry = &fragment_table[fragment]; + if(read_data_block(fragment_entry->start_block, fragment_entry->size, fragment_data) == 0) { + ERROR("read_fragment: failed to read fragment %d\n", fragment); + cached_frag = SQUASHFS_INVALID_FRAG; + return NULL; + } + cached_frag = fragment; + } + + return fragment_data; +} + + +int write_file(char *pathname, unsigned int fragment, unsigned int frag_bytes, unsigned int offset, +unsigned int blocks, long long start, char *block_ptr, unsigned int mode) +{ + unsigned int file_fd, bytes, i; + unsigned int *block_list; + + TRACE("write_file: regular file, blocks %d\n", blocks); + + if((block_list = malloc(blocks * sizeof(unsigned int))) == NULL) { + ERROR("write_file: unable to malloc block list\n"); + return FALSE; + } + + if(swap) { + unsigned int sblock_list[blocks]; + memcpy(sblock_list, block_ptr, blocks * sizeof(unsigned int)); + SQUASHFS_SWAP_INTS(block_list, sblock_list, blocks); + } else + memcpy(block_list, block_ptr, blocks * sizeof(unsigned int)); + + if((file_fd = open(pathname, O_CREAT | O_WRONLY, (mode_t) mode)) == -1) { + ERROR("write_file: failed to create file %s, because %s\n", pathname, + strerror(errno)); + free(block_list); + return FALSE; + } + + for(i = 0; i < blocks; i++) { + if((bytes = read_data_block(start, block_list[i], file_data)) == 0) { + ERROR("write_file: failed to read data block 0x%llx\n", start); + goto failure; + } + + if(write(file_fd, file_data, bytes) < bytes) { + ERROR("write_file: failed to write data block 0x%llx\n", start); + goto failure; + } + + start += SQUASHFS_COMPRESSED_SIZE_BLOCK(block_list[i]); + } + + if(frag_bytes != 0) { + char *fragment_data = read_fragment(fragment); + + if(fragment_data == NULL) + goto failure; + + if(write(file_fd, fragment_data + offset, frag_bytes) < frag_bytes) { + ERROR("write_file: failed to write fragment %d\n", fragment); + goto failure; + } + } + + close(file_fd); + return TRUE; + +failure: + close(file_fd); + free(block_list); + return FALSE; +} + + +int create_inode(char *pathname, unsigned int start_block, unsigned int offset, squashfs_super_block *sBlk) +{ + long long start = sBlk->inode_table_start + start_block; + squashfs_inode_header header; + char *block_ptr; + int bytes = lookup_entry(inode_table_hash, start), file_fd; + + TRACE("create_inode: pathname %s, start 0x%llx, offset %d\n", pathname, start, offset); + + if(bytes == -1) { + ERROR("create_inode: inode block 0x%llx out of range!\n", start); + return FALSE; + } + block_ptr = inode_table + bytes + offset; + + if(swap) { + squashfs_base_inode_header sinode; + memcpy(&sinode, block_ptr, sizeof(header.base)); + SQUASHFS_SWAP_BASE_INODE_HEADER(&header.base, &sinode, sizeof(squashfs_base_inode_header)); + } else + memcpy(&header.base, block_ptr, sizeof(header.base)); + + if(created_inode[header.base.inode_number - 1]) { + TRACE("create_inode: hard link\n"); + if(link(created_inode[header.base.inode_number - 1], pathname) == -1) { + ERROR("create_inode: failed to create hardlink, because %s\n", strerror(errno)); + return FALSE; + } + + return TRUE; + } + + switch(header.base.inode_type) { + case SQUASHFS_FILE_TYPE: { + unsigned int frag_bytes; + unsigned int blocks; + unsigned int offset; + long long start; + squashfs_reg_inode_header *inode = &header.reg; + + if(swap) { + squashfs_reg_inode_header sinode; + memcpy(&sinode, block_ptr, sizeof(sinode)); + SQUASHFS_SWAP_REG_INODE_HEADER(inode, &sinode); + } else + memcpy(inode, block_ptr, sizeof(*inode)); + + frag_bytes = inode->fragment == SQUASHFS_INVALID_FRAG ? 0 : inode->file_size % sBlk->block_size; + offset = inode->offset; + blocks = inode->fragment == SQUASHFS_INVALID_FRAG ? (inode->file_size + + sBlk->block_size - 1) >> sBlk->block_log : inode->file_size >> + sBlk->block_log; + start = inode->start_block; + + TRACE("create_inode: regular file, file_size %lld, blocks %d\n", inode->file_size, blocks); + + if(write_file(pathname, inode->fragment, frag_bytes, offset, blocks, start, + block_ptr + sizeof(*inode), inode->mode)) { + set_attributes(pathname, inode->mode, inode->uid, inode->guid, inode->mtime, FALSE); + file_count ++; + } + break; + } + case SQUASHFS_LREG_TYPE: { + unsigned int frag_bytes; + unsigned int blocks; + unsigned int offset; + long long start; + squashfs_lreg_inode_header *inode = &header.lreg; + + if(swap) { + squashfs_lreg_inode_header sinode; + memcpy(&sinode, block_ptr, sizeof(sinode)); + SQUASHFS_SWAP_LREG_INODE_HEADER(inode, &sinode); + } else + memcpy(inode, block_ptr, sizeof(*inode)); + + frag_bytes = inode->fragment == SQUASHFS_INVALID_FRAG ? 0 : inode->file_size % sBlk->block_size; + offset = inode->offset; + blocks = inode->fragment == SQUASHFS_INVALID_FRAG ? (inode->file_size + + sBlk->block_size - 1) >> sBlk->block_log : inode->file_size >> + sBlk->block_log; + start = inode->start_block; + + TRACE("create_inode: regular file, file_size %lld, blocks %d\n", inode->file_size, blocks); + + if(write_file(pathname, inode->fragment, frag_bytes, offset, blocks, start, + block_ptr + sizeof(*inode), inode->mode)) { + set_attributes(pathname, inode->mode, inode->uid, inode->guid, inode->mtime, FALSE); + file_count ++; + } + break; + } + case SQUASHFS_SYMLINK_TYPE: { + squashfs_symlink_inode_header *inodep = &header.symlink; + char name[65536]; + + if(swap) { + squashfs_symlink_inode_header sinodep; + memcpy(&sinodep, block_ptr, sizeof(sinodep)); + SQUASHFS_SWAP_SYMLINK_INODE_HEADER(inodep, &sinodep); + } else + memcpy(inodep, block_ptr, sizeof(*inodep)); + + TRACE("create_inode: symlink, symlink_size %d\n", inodep->symlink_size); + + strncpy(name, block_ptr + sizeof(squashfs_symlink_inode_header), inodep->symlink_size); + name[inodep->symlink_size] = '\0'; + + if(symlink(name, pathname) == -1) { + ERROR("create_inode: failed to create symlink %s, because %s\n", pathname, + strerror(errno)); + break; + } + + if(geteuid() == 0) { + uid_t uid_value = (uid_t) uid_table[inodep->uid]; + uid_t guid_value = inodep->guid == SQUASHFS_GUIDS ? uid_value : (uid_t) guid_table[inodep->guid]; + + if(lchown(pathname, uid_value, guid_value) == -1) + ERROR("create_inode: failed to change uid and gids on %s, because %s\n", pathname, strerror(errno)); + } + + sym_count ++; + break; + } + case SQUASHFS_BLKDEV_TYPE: + case SQUASHFS_CHRDEV_TYPE: { + squashfs_dev_inode_header *inodep = &header.dev; + + if(swap) { + squashfs_dev_inode_header sinodep; + memcpy(&sinodep, block_ptr, sizeof(sinodep)); + SQUASHFS_SWAP_DEV_INODE_HEADER(inodep, &sinodep); + } else + memcpy(inodep, block_ptr, sizeof(*inodep)); + + TRACE("create_inode: dev, rdev 0x%x\n", inodep->rdev); + + if(geteuid() == 0) { + if(mknod(pathname, inodep->inode_type == SQUASHFS_CHRDEV_TYPE ? S_IFCHR : S_IFBLK, + makedev((inodep->rdev >> 8) & 0xff, inodep->rdev & 0xff)) + == -1) { + ERROR("create_inode: failed to create %s device %s, because %s\n", + inodep->inode_type == SQUASHFS_CHRDEV_TYPE ? "character" : "block", + pathname, strerror(errno)); + break; + } + set_attributes(pathname, inodep->mode, inodep->uid, inodep->guid, inodep->mtime, TRUE); + dev_count ++; + } else + ERROR("create_inode: could not create %s device %s, because you're not superuser!\n", + inodep->inode_type == SQUASHFS_CHRDEV_TYPE ? "character" : "block", + pathname, strerror(errno)); + break; + } + case SQUASHFS_FIFO_TYPE: + TRACE("create_inode: fifo\n"); + + if(mknod(pathname, S_IFIFO, 0) == -1) { + ERROR("create_inode: failed to create fifo %s, because %s\n", + pathname, strerror(errno)); + break; + } + set_attributes(pathname, header.base.mode, header.base.uid, header.base.guid, + header.base.mtime, TRUE); + fifo_count ++; + break; + case SQUASHFS_SOCKET_TYPE: + TRACE("create_inode: socket\n"); + ERROR("create_inode: socket %s ignored\n", pathname); + break; + default: + ERROR("Unknown inode type %d in create_inode_table!\n", header.base.inode_type); + return FALSE; + } + + created_inode[header.base.inode_number - 1] = strdup(pathname); + + return TRUE; +} + + +void uncompress_directory_table(long long start, long long end, squashfs_super_block *sBlk) +{ + int bytes = 0, size = 0, res; + + while(start < end) { + if(size - bytes < SQUASHFS_METADATA_SIZE && (directory_table = realloc(directory_table, size += SQUASHFS_METADATA_SIZE)) == NULL) + EXIT_UNSQUASH("uncompress_directory_table: out of memory in realloc\n"); + TRACE("uncompress_directory_table: reading block 0x%llx\n", start); + add_entry(directory_table_hash, start, bytes); + if((res = read_block(start, &start, directory_table + bytes, sBlk)) == 0) + EXIT_UNSQUASH("uncompress_directory_table: failed to read block\n"); + bytes += res; + } +} + + +#define DIR_ENT_SIZE 16 + +struct dir_ent { + char name[SQUASHFS_NAME_LEN + 1]; + unsigned int start_block; + unsigned int offset; + unsigned int type; +}; + +struct dir { + int dir_count; + int cur_entry; + unsigned int mode; + unsigned int uid; + unsigned int guid; + unsigned int mtime; + struct dir_ent *dirs; +}; + + +struct dir *squashfs_openddir(unsigned int block_start, unsigned int offset, squashfs_super_block *sBlk) +{ + squashfs_dir_header dirh; + char buffer[sizeof(squashfs_dir_entry) + SQUASHFS_NAME_LEN + 1]; + squashfs_dir_entry *dire = (squashfs_dir_entry *) buffer; + long long start = sBlk->inode_table_start + block_start; + char *block_ptr; + int bytes = lookup_entry(inode_table_hash, start); + squashfs_inode_header header; + int dir_count, size; + struct dir_ent *new_dir; + struct dir *dir; + + TRACE("squashfs_opendir: inode start block %d, offset %d\n", block_start, offset); + + if(bytes == -1) { + ERROR("squashfs_opendir: inode block %d not found!\n", block_start); + return NULL; + } + block_ptr = inode_table + bytes + offset; + + if(swap) { + squashfs_dir_inode_header sinode; + memcpy(&sinode, block_ptr, sizeof(header.dir)); + SQUASHFS_SWAP_DIR_INODE_HEADER(&header.dir, &sinode); + } else + memcpy(&header.dir, block_ptr, sizeof(header.dir)); + + switch(header.dir.inode_type) { + case SQUASHFS_DIR_TYPE: + block_start = header.dir.start_block; + offset = header.dir.offset; + size = header.dir.file_size; + break; + case SQUASHFS_LDIR_TYPE: + if(swap) { + squashfs_ldir_inode_header sinode; + memcpy(&sinode, block_ptr, sizeof(header.ldir)); + SQUASHFS_SWAP_LDIR_INODE_HEADER(&header.ldir, &sinode); + } else + memcpy(&header.ldir, block_ptr, sizeof(header.ldir)); + block_start = header.ldir.start_block; + offset = header.ldir.offset; + size = header.ldir.file_size; + break; + default: + ERROR("squashfs_opendir: inode not a directory\n"); + return NULL; + } + + start = sBlk->directory_table_start + block_start; + bytes = lookup_entry(directory_table_hash, start); + + if(bytes == -1) { + ERROR("squashfs_opendir: directory block %d not found!\n", block_start); + return NULL; + } + bytes += offset; + size += bytes - 3; + + if((dir = malloc(sizeof(struct dir))) == NULL) { + ERROR("squashfs_opendir: malloc failed!\n"); + return NULL; + } + + dir->dir_count = 0; + dir->cur_entry = 0; + dir->mode = header.dir.mode; + dir->uid = header.dir.uid; + dir->guid = header.dir.guid; + dir->mtime = header.dir.mtime; + dir->dirs = NULL; + + while(bytes < size) { + if(swap) { + squashfs_dir_header sdirh; + memcpy(&sdirh, directory_table + bytes, sizeof(sdirh)); + SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh); + } else + memcpy(&dirh, directory_table + bytes, sizeof(dirh)); + + dir_count = dirh.count + 1; + TRACE("squashfs_opendir: Read directory header @ byte position %d, %d directory entries\n", bytes, dir_count); + bytes += sizeof(dirh); + + while(dir_count--) { + if(swap) { + squashfs_dir_entry sdire; + memcpy(&sdire, directory_table + bytes, sizeof(sdire)); + SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire); + } else + memcpy(dire, directory_table + bytes, sizeof(dire)); + bytes += sizeof(*dire); + + memcpy(dire->name, directory_table + bytes, dire->size + 1); + dire->name[dire->size + 1] = '\0'; + TRACE("squashfs_opendir: directory entry %s, inode %d:%d, type %d\n", dire->name, dirh.start_block, dire->offset, dire->type); + if((dir->dir_count % DIR_ENT_SIZE) == 0) { + if((new_dir = realloc(dir->dirs, (dir->dir_count + DIR_ENT_SIZE) * sizeof(struct dir_ent))) == NULL) { + ERROR("squashfs_opendir: realloc failed!\n"); + free(dir->dirs); + free(dir); + return NULL; + } + dir->dirs = new_dir; + } + strcpy(dir->dirs[dir->dir_count].name, dire->name); + dir->dirs[dir->dir_count].start_block = dirh.start_block; + dir->dirs[dir->dir_count].offset = dire->offset; + dir->dirs[dir->dir_count].type = dire->type; + dir->dir_count ++; + bytes += dire->size + 1; + } + } + + return dir; +} + + +int squashfs_readdir(struct dir *dir, char **name, unsigned int *start_block, unsigned int *offset, unsigned int *type) +{ + if(dir->cur_entry == dir->dir_count) + return FALSE; + + *name = dir->dirs[dir->cur_entry].name; + *start_block = dir->dirs[dir->cur_entry].start_block; + *offset = dir->dirs[dir->cur_entry].offset; + *type = dir->dirs[dir->cur_entry].type; + dir->cur_entry ++; + + return TRUE; +} + + +void squashfs_closedir(struct dir *dir) +{ + free(dir->dirs); + free(dir); +} + + +int dir_scan(char *parent_name, unsigned int start_block, unsigned int offset, squashfs_super_block *sBlk) +{ + struct dir *dir = squashfs_openddir(start_block, offset, sBlk); + unsigned int type; + char *name, pathname[1024]; + + if(dir == NULL) { + ERROR("dir_scan: Failed to read directory %s (%x:%x)\n", parent_name, start_block, offset); + return FALSE; + } + + if(!lsonly && mkdir(parent_name, (mode_t) dir->mode) == -1) { + ERROR("dir_scan: failed to open directory %s, because %s\n", parent_name, strerror(errno)); + return FALSE; + } + + while(squashfs_readdir(dir, &name, &start_block, &offset, &type)) { + TRACE("dir_scan: name %s, start_block %d, offset %d, type %d\n", name, start_block, offset, type); + + strcat(strcat(strcpy(pathname, parent_name), "/"), name); + + if(lsonly || info) + printf("%s\n", pathname); + + if(type == SQUASHFS_DIR_TYPE) + dir_scan(pathname, start_block, offset, sBlk); + else + if(!lsonly) + create_inode(pathname, start_block, offset, sBlk); + } + + !lsonly && set_attributes(parent_name, dir->mode, dir->uid, dir->guid, dir->mtime, TRUE); + + squashfs_closedir(dir); + dir_count ++; + + return TRUE; +} + + +int read_super(squashfs_super_block *sBlk, char *source) +{ + read_bytes(SQUASHFS_START, sizeof(squashfs_super_block), (char *) sBlk); + + /* Check it is a SQUASHFS superblock */ + swap = 0; + if(sBlk->s_magic != SQUASHFS_MAGIC) { + if(sBlk->s_magic == SQUASHFS_MAGIC_SWAP) { + squashfs_super_block sblk; + ERROR("Reading a different endian SQUASHFS filesystem on %s\n", source); + SQUASHFS_SWAP_SUPER_BLOCK(&sblk, sBlk); + memcpy(sBlk, &sblk, sizeof(squashfs_super_block)); + swap = 1; + } else { + ERROR("Can't find a SQUASHFS superblock on %s\n", source); + goto failed_mount; + } + } + + /* Check the MAJOR & MINOR versions */ + if(sBlk->s_major != SQUASHFS_MAJOR || sBlk->s_minor > SQUASHFS_MINOR) { + ERROR("Major/Minor mismatch, filesystem on %s is (%d:%d)\n", + source, sBlk->s_major, sBlk->s_minor); + ERROR("I only support Squashfs 3.0 filesystems! Later releases will support older Squashfs filesystems\n"); + goto failed_mount; + } + +#if __BYTE_ORDER == __BIG_ENDIAN + TRACE("Found a valid %s endian SQUASHFS superblock on %s.\n", swap ? "little" : "big", source); +#else + TRACE("Found a valid %s endian SQUASHFS superblock on %s.\n", swap ? "big" : "little", source); +#endif + + TRACE("\tInodes are %scompressed\n", SQUASHFS_UNCOMPRESSED_INODES(sBlk->flags) ? "un" : ""); + TRACE("\tData is %scompressed\n", SQUASHFS_UNCOMPRESSED_DATA(sBlk->flags) ? "un" : ""); + TRACE("\tFragments are %scompressed\n", SQUASHFS_UNCOMPRESSED_FRAGMENTS(sBlk->flags) ? "un" : ""); + TRACE("\tCheck data is %s present in the filesystem\n", SQUASHFS_CHECK_DATA(sBlk->flags) ? "" : "not"); + TRACE("\tFragments are %s present in the filesystem\n", SQUASHFS_NO_FRAGMENTS(sBlk->flags) ? "not" : ""); + TRACE("\tAlways_use_fragments option is %s specified\n", SQUASHFS_ALWAYS_FRAGMENTS(sBlk->flags) ? "" : "not"); + TRACE("\tDuplicates are %s removed\n", SQUASHFS_DUPLICATES(sBlk->flags) ? "" : "not"); + TRACE("\tFilesystem size %.2f Kbytes (%.2f Mbytes)\n", sBlk->bytes_used / 1024.0, sBlk->bytes_used / (1024.0 * 1024.0)); + TRACE("\tBlock size %d\n", sBlk->block_size); + TRACE("\tNumber of fragments %d\n", sBlk->fragments); + TRACE("\tNumber of inodes %d\n", sBlk->inodes); + TRACE("\tNumber of uids %d\n", sBlk->no_uids); + TRACE("\tNumber of gids %d\n", sBlk->no_guids); + TRACE("sBlk->inode_table_start 0x%llx\n", sBlk->inode_table_start); + TRACE("sBlk->directory_table_start 0x%llx\n", sBlk->directory_table_start); + TRACE("sBlk->uid_start 0x%llx\n", sBlk->uid_start); + TRACE("sBlk->fragment_table_start 0x%llx\n", sBlk->fragment_table_start); + TRACE("\n"); + + return TRUE; + +failed_mount: + return FALSE; +} + + +#define VERSION() \ + printf("unsquashfs version 1.0 (2006/03/15)\n");\ + printf("copyright (C) 2006 Phillip Lougher \n\n"); \ + printf("This program is free software; you can redistribute it and/or\n");\ + printf("modify it under the terms of the GNU General Public License\n");\ + printf("as published by the Free Software Foundation; either version 2,\n");\ + printf("or (at your option) any later version.\n\n");\ + printf("This program is distributed in the hope that it will be useful,\n");\ + printf("but WITHOUT ANY WARRANTY; without even the implied warranty of\n");\ + printf("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n");\ + printf("GNU General Public License for more details.\n"); +int main(int argc, char *argv[]) +{ + squashfs_super_block sBlk; + char *dest = "squashfs-root"; + int i, version = FALSE; + + for(i = 1; i < argc; i++) { + if(*argv[i] != '-') + break; + if(strcmp(argv[i], "-version") == 0) { + VERSION(); + version = TRUE; + } else if(strcmp(argv[i], "-info") == 0) + info = TRUE; + else if(strcmp(argv[i], "-ls") == 0) + lsonly = TRUE; + else if(strcmp(argv[i], "-dest") == 0) { + if(++i == argc) + goto options; + dest = argv[i]; + } + } + + if(i == argc) { + if(!version) { +options: + ERROR("SYNTAX: %s [-ls | -dest] filesystem\n", argv[0]); + ERROR("\t-version\t\tprint version, licence and copyright information\n"); + ERROR("\t-info\t\t\tprint files as they are unsquashed\n"); + ERROR("\t-ls\t\t\tlist filesystem only\n"); + ERROR("\t-dest \tunsquash to , default \"squashfs-root\"\n"); + } + exit(1); + } + + if((fd = open(argv[i], O_RDONLY)) == -1) { + ERROR("Could not open %s, because %s\n", argv[i], strerror(errno)); + exit(1); + } + + if(read_super(&sBlk, argv[i]) == FALSE) + exit(1); + + block_size = sBlk.block_size; + if((fragment_data = malloc(block_size)) == NULL) + EXIT_UNSQUASH("failed to allocate fragment_data\n"); + + if((file_data = malloc(block_size)) == NULL) + EXIT_UNSQUASH("failed to allocate file_data"); + + if((data = malloc(block_size)) == NULL) + EXIT_UNSQUASH("failed to allocate datan\n"); + + if((created_inode = malloc(sBlk.inodes * sizeof(char *))) == NULL) + EXIT_UNSQUASH("failed to allocate created_inode\n"); + + memset(created_inode, 0, sBlk.inodes * sizeof(char *)); + + read_uids_guids(&sBlk); + read_fragment_table(&sBlk); + uncompress_inode_table(sBlk.inode_table_start, sBlk.directory_table_start, &sBlk); + uncompress_directory_table(sBlk.directory_table_start, sBlk.fragment_table_start, &sBlk); + + dir_scan(dest, SQUASHFS_INODE_BLK(sBlk.root_inode), SQUASHFS_INODE_OFFSET(sBlk.root_inode), &sBlk); + + if(!lsonly) { + printf("\n"); + printf("created %d files\n", file_count); + printf("created %d directories\n", dir_count); + printf("created %d symlinks\n", sym_count); + printf("created %d devices\n", dev_count); + printf("created %d fifos\n", fifo_count); + } + +} -- cgit v1.2.3-54-g00ecf