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); exit(1); } return 0; } return bytes; } else { if(read_bytes(start, c_byte, block) == FALSE) return 0; return c_byte; } }
static int read_data_block(const struct PkgData *pdata, void *buf, const size_t buf_size, const long long offset, const unsigned int c_byte) { const size_t csize = SQUASHFS_COMPRESSED_SIZE_BLOCK(c_byte); return SQUASHFS_COMPRESSED_BLOCK(c_byte) ? read_compressed(pdata, offset, csize, buf, buf_size) : read_uncompressed(pdata, offset, csize, buf, buf_size); }
int scan_inode_table(int fd, long long start, long long end, long long root_inode_start, int root_inode_offset, struct squashfs_super_block *sBlk, union squashfs_inode_header *dir_inode, unsigned char **inode_table, unsigned int *root_inode_block, unsigned int *root_inode_size, long long *uncompressed_file, unsigned int *uncompressed_directory, int *file_count, int *sym_count, int *dev_count, int *dir_count, int *fifo_count, int *sock_count, unsigned int *id_table) { unsigned char *cur_ptr; int byte, files = 0; unsigned int directory_start_block, bytes = 0, size = 0; struct squashfs_base_inode_header base; TRACE("scan_inode_table: start 0x%llx, end 0x%llx, root_inode_start " "0x%llx\n", start, end, root_inode_start); *root_inode_block = UINT_MAX; while(start < end) { if(start == root_inode_start) { TRACE("scan_inode_table: read compressed block 0x%llx " "containing root inode\n", start); *root_inode_block = bytes; } if(size - bytes < SQUASHFS_METADATA_SIZE) { *inode_table = realloc(*inode_table, size += SQUASHFS_METADATA_SIZE); if(*inode_table == NULL) MEM_ERROR(); } TRACE("scan_inode_table: reading block 0x%llx\n", start); byte = read_block(fd, start, &start, 0, *inode_table + bytes); if(byte == 0) goto corrupted; bytes += byte; /* If this is not the last metadata block in the inode table * then it should be SQUASHFS_METADATA_SIZE in size. * Note, we can't use expected in read_block() above for this * because we don't know if this is the last block until * after reading. */ if(start != end && byte != SQUASHFS_METADATA_SIZE) goto corrupted; } /* * We expect to have found the metadata block containing the * root inode in the above inode_table metadata block scan. If it * hasn't been found then the filesystem is corrupted */ if(*root_inode_block == UINT_MAX) goto corrupted; /* * The number of bytes available after the root inode medata block * should be at least the root inode offset + the size of a * regular directory inode, if not the filesystem is corrupted * * +-----------------------+-----------------------+ * | | directory | * | | inode | * +-----------------------+-----------------------+ * ^ ^ ^ * *root_inode_block root_inode_offset bytes */ if((bytes - *root_inode_block) < (root_inode_offset + sizeof(struct squashfs_dir_inode_header))) goto corrupted; /* * Read last inode entry which is the root directory inode, and obtain * the last directory start block index. This is used when calculating * the total uncompressed directory size. The directory bytes in the * last * block will be counted as normal. * * Note, the previous check ensures the following calculation won't * underflow, and we won't access beyond the buffer */ *root_inode_size = bytes - (*root_inode_block + root_inode_offset); bytes = *root_inode_block + root_inode_offset; SQUASHFS_SWAP_DIR_INODE_HEADER(*inode_table + bytes, &dir_inode->dir); if(dir_inode->base.inode_type == SQUASHFS_DIR_TYPE) directory_start_block = dir_inode->dir.start_block; else if(dir_inode->base.inode_type == SQUASHFS_LDIR_TYPE) { if(*root_inode_size < sizeof(struct squashfs_ldir_inode_header)) /* corrupted filesystem */ goto corrupted; SQUASHFS_SWAP_LDIR_INODE_HEADER(*inode_table + bytes, &dir_inode->ldir); directory_start_block = dir_inode->ldir.start_block; } else /* bad type, corrupted filesystem */ goto corrupted; get_uid(id_table[dir_inode->base.uid]); get_guid(id_table[dir_inode->base.guid]); /* allocate fragment to file mapping table */ file_mapping = calloc(sBlk->fragments, sizeof(struct append_file *)); if(file_mapping == NULL) MEM_ERROR(); for(cur_ptr = *inode_table; cur_ptr < *inode_table + bytes; files ++) { if(NO_INODE_BYTES(squashfs_base_inode_header)) /* corrupted filesystem */ goto corrupted; SQUASHFS_SWAP_BASE_INODE_HEADER(cur_ptr, &base); TRACE("scan_inode_table: processing inode @ byte position " "0x%x, type 0x%x\n", (unsigned int) (cur_ptr - *inode_table), base.inode_type); get_uid(id_table[base.uid]); get_guid(id_table[base.guid]); switch(base.inode_type) { case SQUASHFS_FILE_TYPE: { struct squashfs_reg_inode_header inode; int frag_bytes, blocks, i; long long start, file_bytes = 0; unsigned int *block_list; if(NO_INODE_BYTES(squashfs_reg_inode_header)) /* corrupted filesystem */ goto corrupted; SQUASHFS_SWAP_REG_INODE_HEADER(cur_ptr, &inode); frag_bytes = inode.fragment == SQUASHFS_INVALID_FRAG ? 0 : inode.file_size % sBlk->block_size; 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("scan_inode_table: regular file, file_size %d, " "blocks %d\n", inode.file_size, blocks); if(NO_BYTES(blocks * sizeof(unsigned int))) /* corrupted filesystem */ goto corrupted; block_list = malloc(blocks * sizeof(unsigned int)); if(block_list == NULL) MEM_ERROR(); cur_ptr += sizeof(inode); SQUASHFS_SWAP_INTS(cur_ptr, block_list, blocks); *uncompressed_file += inode.file_size; (*file_count) ++; for(i = 0; i < blocks; i++) file_bytes += SQUASHFS_COMPRESSED_SIZE_BLOCK (block_list[i]); if(inode.fragment != SQUASHFS_INVALID_FRAG && inode.fragment >= sBlk->fragments) { free(block_list); goto corrupted; } add_file(start, inode.file_size, file_bytes, block_list, blocks, inode.fragment, inode.offset, frag_bytes); cur_ptr += blocks * sizeof(unsigned int); break; } case SQUASHFS_LREG_TYPE: { struct squashfs_lreg_inode_header inode; int frag_bytes, blocks, i; long long start, file_bytes = 0; unsigned int *block_list; if(NO_INODE_BYTES(squashfs_lreg_inode_header)) /* corrupted filesystem */ goto corrupted; SQUASHFS_SWAP_LREG_INODE_HEADER(cur_ptr, &inode); frag_bytes = inode.fragment == SQUASHFS_INVALID_FRAG ? 0 : inode.file_size % sBlk->block_size; 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("scan_inode_table: extended regular " "file, file_size %lld, blocks %d\n", inode.file_size, blocks); if(NO_BYTES(blocks * sizeof(unsigned int))) /* corrupted filesystem */ goto corrupted; block_list = malloc(blocks * sizeof(unsigned int)); if(block_list == NULL) MEM_ERROR(); cur_ptr += sizeof(inode); SQUASHFS_SWAP_INTS(cur_ptr, block_list, blocks); *uncompressed_file += inode.file_size; (*file_count) ++; for(i = 0; i < blocks; i++) file_bytes += SQUASHFS_COMPRESSED_SIZE_BLOCK (block_list[i]); if(inode.fragment != SQUASHFS_INVALID_FRAG && inode.fragment >= sBlk->fragments) { free(block_list); goto corrupted; } add_file(start, inode.file_size, file_bytes, block_list, blocks, inode.fragment, inode.offset, frag_bytes); cur_ptr += blocks * sizeof(unsigned int); break; } case SQUASHFS_SYMLINK_TYPE: case SQUASHFS_LSYMLINK_TYPE: { struct squashfs_symlink_inode_header inode; if(NO_INODE_BYTES(squashfs_symlink_inode_header)) /* corrupted filesystem */ goto corrupted; SQUASHFS_SWAP_SYMLINK_INODE_HEADER(cur_ptr, &inode); (*sym_count) ++; if (inode.inode_type == SQUASHFS_LSYMLINK_TYPE) { if(NO_BYTES(inode.symlink_size + sizeof(unsigned int))) /* corrupted filesystem */ goto corrupted; cur_ptr += sizeof(inode) + inode.symlink_size + sizeof(unsigned int); } else { if(NO_BYTES(inode.symlink_size)) /* corrupted filesystem */ goto corrupted; cur_ptr += sizeof(inode) + inode.symlink_size; } break; } case SQUASHFS_DIR_TYPE: { struct squashfs_dir_inode_header dir_inode; if(NO_INODE_BYTES(squashfs_dir_inode_header)) /* corrupted filesystem */ goto corrupted; SQUASHFS_SWAP_DIR_INODE_HEADER(cur_ptr, &dir_inode); if(dir_inode.start_block < directory_start_block) *uncompressed_directory += dir_inode.file_size; (*dir_count) ++; cur_ptr += sizeof(struct squashfs_dir_inode_header); break; } case SQUASHFS_LDIR_TYPE: { struct squashfs_ldir_inode_header dir_inode; int i; if(NO_INODE_BYTES(squashfs_ldir_inode_header)) /* corrupted filesystem */ goto corrupted; SQUASHFS_SWAP_LDIR_INODE_HEADER(cur_ptr, &dir_inode); if(dir_inode.start_block < directory_start_block) *uncompressed_directory += dir_inode.file_size; (*dir_count) ++; cur_ptr += sizeof(struct squashfs_ldir_inode_header); for(i = 0; i < dir_inode.i_count; i++) { struct squashfs_dir_index index; if(NO_BYTES(sizeof(index))) /* corrupted filesystem */ goto corrupted; SQUASHFS_SWAP_DIR_INDEX(cur_ptr, &index); if(NO_BYTES(index.size + 1)) /* corrupted filesystem */ goto corrupted; cur_ptr += sizeof(index) + index.size + 1; } break; } case SQUASHFS_BLKDEV_TYPE: case SQUASHFS_CHRDEV_TYPE: if(NO_INODE_BYTES(squashfs_dev_inode_header)) /* corrupted filesystem */ goto corrupted; (*dev_count) ++; cur_ptr += sizeof(struct squashfs_dev_inode_header); break; case SQUASHFS_LBLKDEV_TYPE: case SQUASHFS_LCHRDEV_TYPE: if(NO_INODE_BYTES(squashfs_ldev_inode_header)) /* corrupted filesystem */ goto corrupted; (*dev_count) ++; cur_ptr += sizeof(struct squashfs_ldev_inode_header); break; case SQUASHFS_FIFO_TYPE: if(NO_INODE_BYTES(squashfs_ipc_inode_header)) /* corrupted filesystem */ goto corrupted; (*fifo_count) ++; cur_ptr += sizeof(struct squashfs_ipc_inode_header); break; case SQUASHFS_LFIFO_TYPE: if(NO_INODE_BYTES(squashfs_lipc_inode_header)) /* corrupted filesystem */ goto corrupted; (*fifo_count) ++; cur_ptr += sizeof(struct squashfs_lipc_inode_header); break; case SQUASHFS_SOCKET_TYPE: if(NO_INODE_BYTES(squashfs_ipc_inode_header)) /* corrupted filesystem */ goto corrupted; (*sock_count) ++; cur_ptr += sizeof(struct squashfs_ipc_inode_header); break; case SQUASHFS_LSOCKET_TYPE: if(NO_INODE_BYTES(squashfs_lipc_inode_header)) /* corrupted filesystem */ goto corrupted; (*sock_count) ++; cur_ptr += sizeof(struct squashfs_lipc_inode_header); break; default: ERROR("Unknown inode type %d in scan_inode_table!\n", base.inode_type); goto corrupted; } } printf("Read existing filesystem, %d inodes scanned\n", files); return TRUE; corrupted: ERROR("scan_inode_table: filesystem corruption detected in " "scanning metadata\n"); free(*inode_table); return FALSE; }
static struct file_buffer *get_fragment(struct fragment *fragment, char *data_buffer, int fd) { struct squashfs_fragment_entry *disk_fragment; struct file_buffer *buffer, *compressed_buffer; long long start_block; int res, size, index = fragment->index; char locked; /* * Lookup fragment block in cache. * If the fragment block doesn't exist, then get the compressed version * from the writer cache or off disk, and decompress it. * * This routine has two things which complicate the code: * * 1. Multiple threads can simultaneously lookup/create the * same buffer. This means a buffer needs to be "locked" * when it is being filled in, to prevent other threads from * using it when it is not ready. This is because we now do * fragment duplicate checking in parallel. * 2. We have two caches which need to be checked for the * presence of fragment blocks: the normal fragment cache * and a "reserve" cache. The reserve cache is used to * prevent an unnecessary pipeline stall when the fragment cache * is full of fragments waiting to be compressed. */ pthread_cleanup_push((void *)pthread_mutex_unlock, &dup_mutex); pthread_mutex_lock(&dup_mutex); again: buffer = cache_lookup_nowait(fragment_buffer, index, &locked); if (buffer) { pthread_mutex_unlock(&dup_mutex); if (locked) /* got a buffer being filled in. Wait for it */ cache_wait_unlock(buffer); goto finished; } /* not in fragment cache, is it in the reserve cache? */ buffer = cache_lookup_nowait(reserve_cache, index, &locked); if (buffer) { pthread_mutex_unlock(&dup_mutex); if (locked) /* got a buffer being filled in. Wait for it */ cache_wait_unlock(buffer); goto finished; } /* in neither cache, try to get it from the fragment cache */ buffer = cache_get_nowait(fragment_buffer, index); if (!buffer) { /* * no room, get it from the reserve cache, this is * dimensioned so it will always have space (no more than * processors + 1 can have an outstanding reserve buffer) */ buffer = cache_get_nowait(reserve_cache, index); if (!buffer) { /* failsafe */ ERROR("no space in reserve cache\n"); goto again; } } pthread_mutex_unlock(&dup_mutex); compressed_buffer = cache_lookup(fwriter_buffer, index); pthread_cleanup_push((void *)pthread_mutex_unlock, &fragment_mutex); pthread_mutex_lock(&fragment_mutex); disk_fragment = &fragment_table[index]; size = SQUASHFS_COMPRESSED_SIZE_BLOCK(disk_fragment->size); start_block = disk_fragment->start_block; pthread_cleanup_pop(1); if (SQUASHFS_COMPRESSED_BLOCK(disk_fragment->size)) { int error; char *data; if (compressed_buffer) data = compressed_buffer->data; else { res = read_filesystem(fd, start_block, size, data_buffer); if (res == 0) { ERROR("Failed to read fragment from output" " filesystem\n"); BAD_ERROR("Output filesystem corrupted?\n"); } data = data_buffer; } res = compressor_uncompress(comp, buffer->data, data, size, block_size, &error); if (res == -1) BAD_ERROR("%s uncompress failed with error code %d\n", comp->name, error); } else if (compressed_buffer) memcpy(buffer->data, compressed_buffer->data, size); else { res = read_filesystem(fd, start_block, size, buffer->data); if (res == 0) { ERROR("Failed to read fragment from output " "filesystem\n"); BAD_ERROR("Output filesystem corrupted?\n"); } } cache_unlock(buffer); cache_block_put(compressed_buffer); finished: pthread_cleanup_pop(0); return buffer; }
int scan_inode_table(int fd, long long start, long long end, long long root_inode_start, int root_inode_offset, squashfs_super_block *sBlk, squashfs_inode_header *dir_inode, unsigned char **inode_table, unsigned int *root_inode_block, unsigned int *root_inode_size, long long *uncompressed_file, unsigned int *uncompressed_directory, int *file_count, int *sym_count, int *dev_count, int *dir_count, int *fifo_count, int *sock_count) { unsigned char *cur_ptr; int byte, bytes = 0, size = 0, files = 0; squashfs_reg_inode_header inode; unsigned int directory_start_block; TRACE("scan_inode_table: start 0x%llx, end 0x%llx, root_inode_start 0x%llx\n", start, end, root_inode_start); while(start < end) { if(start == root_inode_start) { TRACE("scan_inode_table: read compressed block 0x%llx containing root inode\n", start); *root_inode_block = bytes; } if((size - bytes < SQUASHFS_METADATA_SIZE) && ((*inode_table = realloc(*inode_table, size += SQUASHFS_METADATA_SIZE)) == NULL)) return FALSE; TRACE("scan_inode_table: reading block 0x%llx\n", start); if((byte = read_block(fd, start, &start, *inode_table + bytes, sBlk)) == 0) { free(*inode_table); return FALSE; } bytes += byte; } /* * Read last inode entry which is the root directory inode, and obtain the last * directory start block index. This is used when calculating the total uncompressed * directory size. The directory bytes in the last block will be counted as normal. * * The root inode is ignored in the inode scan. This ensures there is * always enough bytes left to read a regular file inode entry */ *root_inode_size = bytes - (*root_inode_block + root_inode_offset); bytes = *root_inode_block + root_inode_offset; if(swap) { squashfs_base_inode_header sinode; memcpy(&sinode, *inode_table + bytes, sizeof(dir_inode->base)); SQUASHFS_SWAP_BASE_INODE_HEADER(&dir_inode->base, &sinode, sizeof(squashfs_base_inode_header)); } else memcpy(&dir_inode->base, *inode_table + bytes, sizeof(dir_inode->base)); if(dir_inode->base.inode_type == SQUASHFS_DIR_TYPE) { if(swap) { squashfs_dir_inode_header sinode; memcpy(&sinode, *inode_table + bytes, sizeof(dir_inode->dir)); SQUASHFS_SWAP_DIR_INODE_HEADER(&dir_inode->dir, &sinode); } else memcpy(&dir_inode->dir, *inode_table + bytes, sizeof(dir_inode->dir)); directory_start_block = dir_inode->dir.start_block; } else { if(swap) { squashfs_ldir_inode_header sinode; memcpy(&sinode, *inode_table + bytes, sizeof(dir_inode->ldir)); SQUASHFS_SWAP_LDIR_INODE_HEADER(&dir_inode->ldir, &sinode); } else memcpy(&dir_inode->ldir, *inode_table + bytes, sizeof(dir_inode->ldir)); directory_start_block = dir_inode->ldir.start_block; } for(cur_ptr = *inode_table; cur_ptr < *inode_table + bytes; files ++) { if(swap) { squashfs_reg_inode_header sinode; memcpy(&sinode, cur_ptr, sizeof(inode)); SQUASHFS_SWAP_REG_INODE_HEADER(&inode, &sinode); } else memcpy(&inode, cur_ptr, sizeof(inode)); TRACE("scan_inode_table: processing inode @ byte position 0x%x, type 0x%x\n", cur_ptr - *inode_table, inode.inode_type); switch(inode.inode_type) { case SQUASHFS_FILE_TYPE: { int frag_bytes = inode.fragment == SQUASHFS_INVALID_FRAG ? 0 : inode.file_size % sBlk->block_size; int blocks = inode.fragment == SQUASHFS_INVALID_FRAG ? (inode.file_size + sBlk->block_size - 1) >> sBlk->block_log : inode.file_size >> sBlk->block_log; long long file_bytes = 0; int i; long long start = inode.start_block; unsigned int *block_list; TRACE("scan_inode_table: regular file, file_size %lld, blocks %d\n", inode.file_size, blocks); if((block_list = malloc(blocks * sizeof(unsigned int))) == NULL) { ERROR("Out of memory in block list malloc\n"); goto failed; } cur_ptr += sizeof(inode); if(swap) { unsigned int sblock_list[blocks]; memcpy(sblock_list, cur_ptr, blocks * sizeof(unsigned int)); SQUASHFS_SWAP_INTS(block_list, sblock_list, blocks); } else memcpy(block_list, cur_ptr, blocks * sizeof(unsigned int)); *uncompressed_file += inode.file_size; (*file_count) ++; for(i = 0; i < blocks; i++) file_bytes += SQUASHFS_COMPRESSED_SIZE_BLOCK(block_list[i]); add_file(start, inode.file_size, file_bytes, block_list, blocks, inode.fragment, inode.offset, frag_bytes); cur_ptr += blocks * sizeof(unsigned int); break; } case SQUASHFS_LREG_TYPE: { squashfs_lreg_inode_header inode; int frag_bytes; int blocks; long long file_bytes = 0; int i; long long start; unsigned int *block_list; if(swap) { squashfs_lreg_inode_header sinodep; memcpy(&sinodep, cur_ptr, sizeof(sinodep)); SQUASHFS_SWAP_LREG_INODE_HEADER(&inode, &sinodep); } else memcpy(&inode, cur_ptr, sizeof(inode)); TRACE("scan_inode_table: extended regular file, file_size %lld, blocks %d\n", inode.file_size, blocks); cur_ptr += sizeof(inode); frag_bytes = inode.fragment == SQUASHFS_INVALID_FRAG ? 0 : inode.file_size % sBlk->block_size; 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; if((block_list = malloc(blocks * sizeof(unsigned int))) == NULL) { ERROR("Out of memory in block list malloc\n"); goto failed; } if(swap) { unsigned int sblock_list[blocks]; memcpy(sblock_list, cur_ptr, blocks * sizeof(unsigned int)); SQUASHFS_SWAP_INTS(block_list, sblock_list, blocks); } else memcpy(block_list, cur_ptr, blocks * sizeof(unsigned int)); *uncompressed_file += inode.file_size; (*file_count) ++; for(i = 0; i < blocks; i++) file_bytes += SQUASHFS_COMPRESSED_SIZE_BLOCK(block_list[i]); add_file(start, inode.file_size, file_bytes, block_list, blocks, inode.fragment, inode.offset, frag_bytes); cur_ptr += blocks * sizeof(unsigned int); break; } case SQUASHFS_SYMLINK_TYPE: { squashfs_symlink_inode_header inodep; if(swap) { squashfs_symlink_inode_header sinodep; memcpy(&sinodep, cur_ptr, sizeof(sinodep)); SQUASHFS_SWAP_SYMLINK_INODE_HEADER(&inodep, &sinodep); } else memcpy(&inodep, cur_ptr, sizeof(inodep)); (*sym_count) ++; cur_ptr += sizeof(inodep) + inodep.symlink_size; break; } case SQUASHFS_DIR_TYPE: { squashfs_dir_inode_header dir_inode; if(swap) { squashfs_dir_inode_header sinode; memcpy(&sinode, cur_ptr, sizeof(dir_inode)); SQUASHFS_SWAP_DIR_INODE_HEADER(&dir_inode, &sinode); } else memcpy(&dir_inode, cur_ptr, sizeof(dir_inode)); if(dir_inode.start_block < directory_start_block) *uncompressed_directory += dir_inode.file_size; (*dir_count) ++; cur_ptr += sizeof(squashfs_dir_inode_header); break; } case SQUASHFS_LDIR_TYPE: { squashfs_ldir_inode_header dir_inode; int i; if(swap) { squashfs_ldir_inode_header sinode; memcpy(&sinode, cur_ptr, sizeof(dir_inode)); SQUASHFS_SWAP_LDIR_INODE_HEADER(&dir_inode, &sinode); } else memcpy(&dir_inode, cur_ptr, sizeof(dir_inode)); if(dir_inode.start_block < directory_start_block) *uncompressed_directory += dir_inode.file_size; (*dir_count) ++; cur_ptr += sizeof(squashfs_ldir_inode_header); for(i = 0; i < dir_inode.i_count; i++) { squashfs_dir_index index; if(swap) { squashfs_dir_index sindex; memcpy(&sindex, cur_ptr, sizeof(squashfs_dir_index)); SQUASHFS_SWAP_DIR_INDEX(&index, &sindex); } else memcpy(&index, cur_ptr, sizeof(squashfs_dir_index)); cur_ptr += sizeof(squashfs_dir_index) + index.size + 1; } break; } case SQUASHFS_BLKDEV_TYPE: case SQUASHFS_CHRDEV_TYPE: (*dev_count) ++; cur_ptr += sizeof(squashfs_dev_inode_header); break; case SQUASHFS_FIFO_TYPE: (*fifo_count) ++; cur_ptr += sizeof(squashfs_ipc_inode_header); break; case SQUASHFS_SOCKET_TYPE: (*sock_count) ++; cur_ptr += sizeof(squashfs_ipc_inode_header); break; default: ERROR("Unknown inode type %d in scan_inode_table!\n", inode.inode_type); goto failed; } } return files; failed: free(*inode_table); return FALSE; }
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; }
/* * Read and decompress a metadata block or datablock. Length is non-zero * if a datablock is being read (the size is stored elsewhere in the * filesystem), otherwise the length is obtained from the first two bytes of * the metadata block. A bit in the length field indicates if the block * is stored uncompressed in the filesystem (usually because compression * generated a larger block - this does occasionally happen with compression * algorithms). */ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index, int length, u64 *next_index, int srclength, int pages) { struct squashfs_sb_info *msblk = sb->s_fs_info; struct buffer_head **bh; int offset = index & ((1 << msblk->devblksize_log2) - 1); u64 cur_index = index >> msblk->devblksize_log2; int bytes, compressed, b = 0, k = 0, page = 0, avail; bh = kcalloc(((srclength + msblk->devblksize - 1) >> msblk->devblksize_log2) + 1, sizeof(*bh), GFP_KERNEL); if (bh == NULL) return -ENOMEM; if (length) { /* * Datablock. */ bytes = -offset; compressed = SQUASHFS_COMPRESSED_BLOCK(length); length = SQUASHFS_COMPRESSED_SIZE_BLOCK(length); if (next_index) *next_index = index + length; TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n", index, compressed ? "" : "un", length, srclength); if (length < 0 || length > srclength || (index + length) > msblk->bytes_used) goto read_failure; for (b = 0; bytes < length; b++, cur_index++) { bh[b] = sb_getblk(sb, cur_index); if (bh[b] == NULL) goto block_release; bytes += msblk->devblksize; } ll_rw_block(READ, b, bh); } else { /* * Metadata block. */ if ((index + 2) > msblk->bytes_used) goto read_failure; bh[0] = get_block_length(sb, &cur_index, &offset, &length); if (bh[0] == NULL) goto read_failure; b = 1; bytes = msblk->devblksize - offset; compressed = SQUASHFS_COMPRESSED(length); length = SQUASHFS_COMPRESSED_SIZE(length); if (next_index) *next_index = index + length + 2; TRACE("Block @ 0x%llx, %scompressed size %d\n", index, compressed ? "" : "un", length); if (length < 0 || length > srclength || (index + length) > msblk->bytes_used) goto block_release; for (; bytes < length; b++) { bh[b] = sb_getblk(sb, ++cur_index); if (bh[b] == NULL) goto block_release; bytes += msblk->devblksize; } ll_rw_block(READ, b - 1, bh + 1); } if (compressed) { length = squashfs_decompress(msblk, buffer, bh, b, offset, length, srclength, pages); if (length < 0) goto read_failure; } else { /* * Block is uncompressed. */ int i, in, pg_offset = 0; for (i = 0; i < b; i++) { wait_on_buffer(bh[i]); if (!buffer_uptodate(bh[i])) goto block_release; } for (bytes = length; k < b; k++) { in = min(bytes, msblk->devblksize - offset); bytes -= in; while (in) { if (pg_offset == PAGE_CACHE_SIZE) { page++; pg_offset = 0; } avail = min_t(int, in, PAGE_CACHE_SIZE - pg_offset); memcpy(buffer[page] + pg_offset, bh[k]->b_data + offset, avail); in -= avail; pg_offset += avail; offset += avail; } offset = 0; put_bh(bh[k]); } } kfree(bh); return length; block_release: for (; k < b; k++) put_bh(bh[k]); read_failure: ERROR("squashfs_read_data failed to read block 0x%llx\n", (unsigned long long) index); kfree(bh); return -EIO; }