struct dir *squashfs_opendir_1(unsigned int block_start, unsigned int offset, struct inode **i) { squashfs_dir_header_2 dirh; char buffer[sizeof(squashfs_dir_entry_2) + SQUASHFS_NAME_LEN + 1] __attribute__((aligned)); squashfs_dir_entry_2 *dire = (squashfs_dir_entry_2 *) buffer; long long start; int bytes; 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); *i = s_ops.read_inode(block_start, offset); start = sBlk.s.directory_table_start + (*i)->start; bytes = lookup_entry(directory_table_hash, start); if(bytes == -1) EXIT_UNSQUASH("squashfs_opendir: directory block %d not " "found!\n", block_start); bytes += (*i)->offset; size = (*i)->data + bytes; if((dir = malloc(sizeof(struct dir))) == NULL) EXIT_UNSQUASH("squashfs_opendir: malloc failed!\n"); dir->dir_count = 0; dir->cur_entry = 0; dir->mode = (*i)->mode; dir->uid = (*i)->uid; dir->guid = (*i)->gid; dir->mtime = (*i)->time; dir->xattr = (*i)->xattr; dir->dirs = NULL; while(bytes < size) { if(swap) { squashfs_dir_header_2 sdirh; memcpy(&sdirh, directory_table + bytes, sizeof(sdirh)); SQUASHFS_SWAP_DIR_HEADER_2(&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_2 sdire; memcpy(&sdire, directory_table + bytes, sizeof(sdire)); SQUASHFS_SWAP_DIR_ENTRY_2(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) { new_dir = realloc(dir->dirs, (dir->dir_count + DIR_ENT_SIZE) * sizeof(struct dir_ent)); if(new_dir == NULL) EXIT_UNSQUASH("squashfs_opendir: " "realloc failed!\n"); 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; }
static int squashfs_readdir_2(struct file *file, void *dirent, filldir_t filldir) { struct inode *i = file->f_dentry->d_inode; struct squashfs_sb_info *msblk = i->i_sb->s_fs_info; struct squashfs_super_block *sblk = &msblk->sblk; long long next_block = SQUASHFS_I(i)->start_block + sblk->directory_table_start; int next_offset = SQUASHFS_I(i)->offset, length = 0, dir_count; struct squashfs_dir_header_2 dirh; struct squashfs_dir_entry_2 *dire; TRACE("Entered squashfs_readdir_2 [%llx:%x]\n", next_block, next_offset); if (!(dire = kmalloc(sizeof(struct squashfs_dir_entry) + SQUASHFS_NAME_LEN + 1, GFP_KERNEL))) { ERROR("Failed to allocate squashfs_dir_entry\n"); goto finish; } length = get_dir_index_using_offset(i->i_sb, &next_block, &next_offset, SQUASHFS_I(i)->u.s2.directory_index_start, SQUASHFS_I(i)->u.s2.directory_index_offset, SQUASHFS_I(i)->u.s2.directory_index_count, file->f_pos); while (length < i_size_read(i)) { /* read directory header */ if (msblk->swap) { struct squashfs_dir_header_2 sdirh; if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh, next_block, next_offset, sizeof(sdirh), &next_block, &next_offset)) goto failed_read; length += sizeof(sdirh); SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh); } else { if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh, next_block, next_offset, sizeof(dirh), &next_block, &next_offset)) goto failed_read; length += sizeof(dirh); } dir_count = dirh.count + 1; while (dir_count--) { if (msblk->swap) { struct squashfs_dir_entry_2 sdire; if (!squashfs_get_cached_block(i->i_sb, (char *) &sdire, next_block, next_offset, sizeof(sdire), &next_block, &next_offset)) goto failed_read; length += sizeof(sdire); SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire); } else { if (!squashfs_get_cached_block(i->i_sb, (char *) dire, next_block, next_offset, sizeof(*dire), &next_block, &next_offset)) goto failed_read; length += sizeof(*dire); } if (!squashfs_get_cached_block(i->i_sb, dire->name, next_block, next_offset, dire->size + 1, &next_block, &next_offset)) goto failed_read; length += dire->size + 1; if (file->f_pos >= length) continue; dire->name[dire->size + 1] = '\0'; TRACE("Calling filldir(%x, %s, %d, %d, %x:%x, %d)\n", (unsigned int) dirent, dire->name, dire->size + 1, (int) file->f_pos, dirh.start_block, dire->offset, squashfs_filetype_table[dire->type]); if (filldir(dirent, dire->name, dire->size + 1, file->f_pos, SQUASHFS_MK_VFS_INODE( dirh.start_block, dire->offset), squashfs_filetype_table[dire->type]) < 0) { TRACE("Filldir returned less than 0\n"); goto finish; } file->f_pos = length; } } finish: kfree(dire); return 0; failed_read: ERROR("Unable to read directory block [%llx:%x]\n", next_block, next_offset); kfree(dire); return 0; }
static struct dentry *squashfs_lookup_2(struct inode *i, struct dentry *dentry, struct nameidata *nd) { const unsigned char *name = dentry->d_name.name; int len = dentry->d_name.len; struct inode *inode = NULL; struct squashfs_sb_info *msblk = i->i_sb->s_fs_info; struct squashfs_super_block *sblk = &msblk->sblk; long long next_block = SQUASHFS_I(i)->start_block + sblk->directory_table_start; int next_offset = SQUASHFS_I(i)->offset, length = 0, dir_count; struct squashfs_dir_header_2 dirh; struct squashfs_dir_entry_2 *dire; int sorted = sblk->s_major == 2 && sblk->s_minor >= 1; TRACE("Entered squashfs_lookup_2 [%llx:%x]\n", next_block, next_offset); if (!(dire = kmalloc(sizeof(struct squashfs_dir_entry) + SQUASHFS_NAME_LEN + 1, GFP_KERNEL))) { ERROR("Failed to allocate squashfs_dir_entry\n"); goto exit_loop; } if (len > SQUASHFS_NAME_LEN) goto exit_loop; length = get_dir_index_using_name(i->i_sb, &next_block, &next_offset, SQUASHFS_I(i)->u.s2.directory_index_start, SQUASHFS_I(i)->u.s2.directory_index_offset, SQUASHFS_I(i)->u.s2.directory_index_count, name, len); while (length < i_size_read(i)) { /* read directory header */ if (msblk->swap) { struct squashfs_dir_header_2 sdirh; if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh, next_block, next_offset, sizeof(sdirh), &next_block, &next_offset)) goto failed_read; length += sizeof(sdirh); SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh); } else { if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh, next_block, next_offset, sizeof(dirh), &next_block, &next_offset)) goto failed_read; length += sizeof(dirh); } dir_count = dirh.count + 1; while (dir_count--) { if (msblk->swap) { struct squashfs_dir_entry_2 sdire; if (!squashfs_get_cached_block(i->i_sb, (char *) &sdire, next_block,next_offset, sizeof(sdire), &next_block, &next_offset)) goto failed_read; length += sizeof(sdire); SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire); } else { if (!squashfs_get_cached_block(i->i_sb, (char *) dire, next_block,next_offset, sizeof(*dire), &next_block, &next_offset)) goto failed_read; length += sizeof(*dire); } if (!squashfs_get_cached_block(i->i_sb, dire->name, next_block, next_offset, dire->size + 1, &next_block, &next_offset)) goto failed_read; length += dire->size + 1; if (sorted && name[0] < dire->name[0]) goto exit_loop; if ((len == dire->size + 1) && !strncmp(name, dire->name, len)) { squashfs_inode_t ino = SQUASHFS_MKINODE(dirh.start_block, dire->offset); unsigned int inode_number = SQUASHFS_MK_VFS_INODE(dirh.start_block, dire->offset); TRACE("calling squashfs_iget for directory " "entry %s, inode %x:%x, %lld\n", name, dirh.start_block, dire->offset, ino); inode = squashfs_iget(i->i_sb, ino, inode_number); goto exit_loop; } } } exit_loop: kfree(dire); d_add(dentry, inode); return ERR_PTR(0); failed_read: ERROR("Unable to read directory block [%llx:%x]\n", next_block, next_offset); goto exit_loop; }
struct dir *squashfs_opendir_1(unsigned int block_start, unsigned int offset, struct inode **i) { squashfs_dir_header_2 dirh; char buffer[sizeof(squashfs_dir_entry_2) + SQUASHFS_NAME_LEN + 1] __attribute__((aligned)); squashfs_dir_entry_2 *dire = (squashfs_dir_entry_2 *) buffer; long long start; int bytes; 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); *i = s_ops.read_inode(block_start, offset); dir = malloc(sizeof(struct dir)); if(dir == NULL) EXIT_UNSQUASH("squashfs_opendir: malloc failed!\n"); dir->dir_count = 0; dir->cur_entry = 0; dir->mode = (*i)->mode; dir->uid = (*i)->uid; dir->guid = (*i)->gid; dir->mtime = (*i)->time; dir->xattr = (*i)->xattr; dir->dirs = NULL; if ((*i)->data == 0) /* * if the directory is empty, skip the unnecessary * lookup_entry, this fixes the corner case with * completely empty filesystems where lookup_entry correctly * returning -1 is incorrectly treated as an error */ return dir; start = sBlk.s.directory_table_start + (*i)->start; bytes = lookup_entry(directory_table_hash, start); if(bytes == -1) EXIT_UNSQUASH("squashfs_opendir: directory block %d not " "found!\n", block_start); bytes += (*i)->offset; size = (*i)->data + bytes; while(bytes < size) { if(swap) { squashfs_dir_header_2 sdirh; memcpy(&sdirh, directory_table + bytes, sizeof(sdirh)); SQUASHFS_SWAP_DIR_HEADER_2(&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); /* dir_count should never be larger than 256 */ if(dir_count > 256) goto corrupted; while(dir_count--) { if(swap) { squashfs_dir_entry_2 sdire; memcpy(&sdire, directory_table + bytes, sizeof(sdire)); SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire); } else memcpy(dire, directory_table + bytes, sizeof(*dire)); bytes += sizeof(*dire); /* size should never be larger than SQUASHFS_NAME_LEN */ if(dire->size > SQUASHFS_NAME_LEN) goto corrupted; 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) { new_dir = realloc(dir->dirs, (dir->dir_count + DIR_ENT_SIZE) * sizeof(struct dir_ent)); if(new_dir == NULL) EXIT_UNSQUASH("squashfs_opendir: " "realloc failed!\n"); 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; corrupted: free(dir->dirs); free(dir); return NULL; }