/*
 * Initialise VFS inode by reading inode from inode table (compressed
 * metadata).  The format and amount of data read depends on type.
 */
int squashfs_read_inode(struct inode *inode, long long ino)
{
	struct super_block *sb = inode->i_sb;
	struct squashfs_sb_info *msblk = sb->s_fs_info;
	u64 block = SQUASHFS_INODE_BLK(ino) + msblk->inode_table;
	int err, type, offset = SQUASHFS_INODE_OFFSET(ino);
	union squashfs_inode squashfs_ino;
	struct squashfs_base_inode *sqshb_ino = &squashfs_ino.base;
	int xattr_id = SQUASHFS_INVALID_XATTR;

	TRACE("Entered squashfs_read_inode\n");

	/*
	 * Read inode base common to all inode types.
	 */
	err = squashfs_read_metadata(sb, sqshb_ino, &block,
				&offset, sizeof(*sqshb_ino));
	if (err < 0)
		goto failed_read;

	err = squashfs_new_inode(sb, inode, sqshb_ino);
	if (err)
		goto failed_read;

	block = SQUASHFS_INODE_BLK(ino) + msblk->inode_table;
	offset = SQUASHFS_INODE_OFFSET(ino);

	type = le16_to_cpu(sqshb_ino->inode_type);
	switch (type) {
	case SQUASHFS_REG_TYPE: {
		unsigned int frag_offset, frag;
		int frag_size;
		u64 frag_blk;
		struct squashfs_reg_inode *sqsh_ino = &squashfs_ino.reg;

		err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
							sizeof(*sqsh_ino));
		if (err < 0)
			goto failed_read;

		frag = le32_to_cpu(sqsh_ino->fragment);
		if (frag != SQUASHFS_INVALID_FRAG) {
			frag_offset = le32_to_cpu(sqsh_ino->offset);
			frag_size = squashfs_frag_lookup(sb, frag, &frag_blk);
			if (frag_size < 0) {
				err = frag_size;
				goto failed_read;
			}
		} else {
			frag_blk = SQUASHFS_INVALID_BLK;
			frag_size = 0;
			frag_offset = 0;
		}

		set_nlink(inode, 1);
		inode->i_size = le32_to_cpu(sqsh_ino->file_size);
		inode->i_fop = &generic_ro_fops;
		inode->i_mode |= S_IFREG;
		inode->i_blocks = ((inode->i_size - 1) >> 9) + 1;
		squashfs_i(inode)->fragment_block = frag_blk;
		squashfs_i(inode)->fragment_size = frag_size;
		squashfs_i(inode)->fragment_offset = frag_offset;
		squashfs_i(inode)->start = le32_to_cpu(sqsh_ino->start_block);
		squashfs_i(inode)->block_list_start = block;
		squashfs_i(inode)->offset = offset;
		inode->i_data.a_ops = &squashfs_aops;

		TRACE("File inode %x:%x, start_block %llx, block_list_start "
			"%llx, offset %x\n", SQUASHFS_INODE_BLK(ino),
			offset, squashfs_i(inode)->start, block, offset);
		break;
	}
	case SQUASHFS_LREG_TYPE: {
		unsigned int frag_offset, frag;
		int frag_size;
		u64 frag_blk;
		struct squashfs_lreg_inode *sqsh_ino = &squashfs_ino.lreg;

		err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
							sizeof(*sqsh_ino));
		if (err < 0)
			goto failed_read;

		frag = le32_to_cpu(sqsh_ino->fragment);
		if (frag != SQUASHFS_INVALID_FRAG) {
			frag_offset = le32_to_cpu(sqsh_ino->offset);
			frag_size = squashfs_frag_lookup(sb, frag, &frag_blk);
			if (frag_size < 0) {
				err = frag_size;
				goto failed_read;
			}
		} else {
			frag_blk = SQUASHFS_INVALID_BLK;
			frag_size = 0;
			frag_offset = 0;
		}

		xattr_id = le32_to_cpu(sqsh_ino->xattr);
		set_nlink(inode, le32_to_cpu(sqsh_ino->nlink));
		inode->i_size = le64_to_cpu(sqsh_ino->file_size);
		inode->i_op = &squashfs_inode_ops;
		inode->i_fop = &generic_ro_fops;
		inode->i_mode |= S_IFREG;
		inode->i_blocks = (inode->i_size -
				le64_to_cpu(sqsh_ino->sparse) + 511) >> 9;

		squashfs_i(inode)->fragment_block = frag_blk;
		squashfs_i(inode)->fragment_size = frag_size;
		squashfs_i(inode)->fragment_offset = frag_offset;
		squashfs_i(inode)->start = le64_to_cpu(sqsh_ino->start_block);
		squashfs_i(inode)->block_list_start = block;
		squashfs_i(inode)->offset = offset;
		inode->i_data.a_ops = &squashfs_aops;

		TRACE("File inode %x:%x, start_block %llx, block_list_start "
			"%llx, offset %x\n", SQUASHFS_INODE_BLK(ino),
			offset, squashfs_i(inode)->start, block, offset);
		break;
	}
	case SQUASHFS_DIR_TYPE: {
		struct squashfs_dir_inode *sqsh_ino = &squashfs_ino.dir;

		err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
				sizeof(*sqsh_ino));
		if (err < 0)
			goto failed_read;

		set_nlink(inode, le32_to_cpu(sqsh_ino->nlink));
		inode->i_size = le16_to_cpu(sqsh_ino->file_size);
		inode->i_op = &squashfs_dir_inode_ops;
		inode->i_fop = &squashfs_dir_ops;
		inode->i_mode |= S_IFDIR;
		squashfs_i(inode)->start = le32_to_cpu(sqsh_ino->start_block);
		squashfs_i(inode)->offset = le16_to_cpu(sqsh_ino->offset);
		squashfs_i(inode)->dir_idx_cnt = 0;
		squashfs_i(inode)->parent = le32_to_cpu(sqsh_ino->parent_inode);

		TRACE("Directory inode %x:%x, start_block %llx, offset %x\n",
				SQUASHFS_INODE_BLK(ino), offset,
				squashfs_i(inode)->start,
				le16_to_cpu(sqsh_ino->offset));
		break;
	}
	case SQUASHFS_LDIR_TYPE: {
		struct squashfs_ldir_inode *sqsh_ino = &squashfs_ino.ldir;

		err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
				sizeof(*sqsh_ino));
		if (err < 0)
			goto failed_read;

		xattr_id = le32_to_cpu(sqsh_ino->xattr);
		set_nlink(inode, le32_to_cpu(sqsh_ino->nlink));
		inode->i_size = le32_to_cpu(sqsh_ino->file_size);
		inode->i_op = &squashfs_dir_inode_ops;
		inode->i_fop = &squashfs_dir_ops;
		inode->i_mode |= S_IFDIR;
		squashfs_i(inode)->start = le32_to_cpu(sqsh_ino->start_block);
		squashfs_i(inode)->offset = le16_to_cpu(sqsh_ino->offset);
		squashfs_i(inode)->dir_idx_start = block;
		squashfs_i(inode)->dir_idx_offset = offset;
		squashfs_i(inode)->dir_idx_cnt = le16_to_cpu(sqsh_ino->i_count);
		squashfs_i(inode)->parent = le32_to_cpu(sqsh_ino->parent_inode);

		TRACE("Long directory inode %x:%x, start_block %llx, offset "
				"%x\n", SQUASHFS_INODE_BLK(ino), offset,
				squashfs_i(inode)->start,
				le16_to_cpu(sqsh_ino->offset));
		break;
	}
	case SQUASHFS_SYMLINK_TYPE:
	case SQUASHFS_LSYMLINK_TYPE: {
		struct squashfs_symlink_inode *sqsh_ino = &squashfs_ino.symlink;

		err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
				sizeof(*sqsh_ino));
		if (err < 0)
			goto failed_read;

		set_nlink(inode, le32_to_cpu(sqsh_ino->nlink));
		inode->i_size = le32_to_cpu(sqsh_ino->symlink_size);
		inode->i_op = &squashfs_symlink_inode_ops;
		inode->i_data.a_ops = &squashfs_symlink_aops;
		inode->i_mode |= S_IFLNK;
		squashfs_i(inode)->start = block;
		squashfs_i(inode)->offset = offset;

		if (type == SQUASHFS_LSYMLINK_TYPE) {
			__le32 xattr;

			err = squashfs_read_metadata(sb, NULL, &block,
						&offset, inode->i_size);
			if (err < 0)
				goto failed_read;
			err = squashfs_read_metadata(sb, &xattr, &block,
						&offset, sizeof(xattr));
			if (err < 0)
				goto failed_read;
			xattr_id = le32_to_cpu(xattr);
		}

		TRACE("Symbolic link inode %x:%x, start_block %llx, offset "
				"%x\n", SQUASHFS_INODE_BLK(ino), offset,
				block, offset);
		break;
	}
	case SQUASHFS_BLKDEV_TYPE:
	case SQUASHFS_CHRDEV_TYPE: {
		struct squashfs_dev_inode *sqsh_ino = &squashfs_ino.dev;
		unsigned int rdev;

		err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
				sizeof(*sqsh_ino));
		if (err < 0)
			goto failed_read;

		if (type == SQUASHFS_CHRDEV_TYPE)
			inode->i_mode |= S_IFCHR;
		else
			inode->i_mode |= S_IFBLK;
		set_nlink(inode, le32_to_cpu(sqsh_ino->nlink));
		rdev = le32_to_cpu(sqsh_ino->rdev);
		init_special_inode(inode, inode->i_mode, new_decode_dev(rdev));

		TRACE("Device inode %x:%x, rdev %x\n",
				SQUASHFS_INODE_BLK(ino), offset, rdev);
		break;
	}
	case SQUASHFS_LBLKDEV_TYPE:
	case SQUASHFS_LCHRDEV_TYPE: {
		struct squashfs_ldev_inode *sqsh_ino = &squashfs_ino.ldev;
		unsigned int rdev;

		err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
				sizeof(*sqsh_ino));
		if (err < 0)
			goto failed_read;

		if (type == SQUASHFS_LCHRDEV_TYPE)
			inode->i_mode |= S_IFCHR;
		else
			inode->i_mode |= S_IFBLK;
		xattr_id = le32_to_cpu(sqsh_ino->xattr);
		inode->i_op = &squashfs_inode_ops;
		set_nlink(inode, le32_to_cpu(sqsh_ino->nlink));
		rdev = le32_to_cpu(sqsh_ino->rdev);
		init_special_inode(inode, inode->i_mode, new_decode_dev(rdev));

		TRACE("Device inode %x:%x, rdev %x\n",
				SQUASHFS_INODE_BLK(ino), offset, rdev);
		break;
	}
	case SQUASHFS_FIFO_TYPE:
	case SQUASHFS_SOCKET_TYPE: {
		struct squashfs_ipc_inode *sqsh_ino = &squashfs_ino.ipc;

		err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
				sizeof(*sqsh_ino));
		if (err < 0)
			goto failed_read;

		if (type == SQUASHFS_FIFO_TYPE)
			inode->i_mode |= S_IFIFO;
		else
			inode->i_mode |= S_IFSOCK;
		set_nlink(inode, le32_to_cpu(sqsh_ino->nlink));
		init_special_inode(inode, inode->i_mode, 0);
		break;
	}
	case SQUASHFS_LFIFO_TYPE:
	case SQUASHFS_LSOCKET_TYPE: {
		struct squashfs_lipc_inode *sqsh_ino = &squashfs_ino.lipc;

		err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
				sizeof(*sqsh_ino));
		if (err < 0)
			goto failed_read;

		if (type == SQUASHFS_LFIFO_TYPE)
			inode->i_mode |= S_IFIFO;
		else
			inode->i_mode |= S_IFSOCK;
		xattr_id = le32_to_cpu(sqsh_ino->xattr);
		inode->i_op = &squashfs_inode_ops;
		set_nlink(inode, le32_to_cpu(sqsh_ino->nlink));
		init_special_inode(inode, inode->i_mode, 0);
		break;
	}
	default:
		ERROR("Unknown inode type %d in squashfs_iget!\n", type);
		return -EINVAL;
	}

	if (xattr_id != SQUASHFS_INVALID_XATTR && msblk->xattr_id_table) {
		err = squashfs_xattr_lookup(sb, xattr_id,
					&squashfs_i(inode)->xattr_count,
					&squashfs_i(inode)->xattr_size,
					&squashfs_i(inode)->xattr);
		if (err < 0)
			goto failed_read;
		inode->i_blocks += ((squashfs_i(inode)->xattr_size - 1) >> 9)
				+ 1;
	} else
Beispiel #2
0
static int squashfs_read_inode_2(struct inode *i, squashfs_inode_t inode)
{
    struct super_block *s = i->i_sb;
    struct squashfs_sb_info *msblk = s->s_fs_info;
    struct squashfs_super_block *sblk = &msblk->sblk;
    unsigned int block = SQUASHFS_INODE_BLK(inode) +
                         sblk->inode_table_start;
    unsigned int offset = SQUASHFS_INODE_OFFSET(inode);
    unsigned int ino = i->i_ino;
    long long next_block;
    unsigned int next_offset;
    union squashfs_inode_header_2 id, sid;
    struct squashfs_base_inode_header_2 *inodeb = &id.base,
                                             *sinodeb = &sid.base;

    TRACE("Entered squashfs_iget\n");

    if (msblk->swap) {
        if (!squashfs_get_cached_block(s, (char *) sinodeb, block,
                                       offset, sizeof(*sinodeb), &next_block,
                                       &next_offset))
            goto failed_read;
        SQUASHFS_SWAP_BASE_INODE_HEADER_2(inodeb, sinodeb,
                                          sizeof(*sinodeb));
    } else if (!squashfs_get_cached_block(s, (char *) inodeb, block,
                                          offset, sizeof(*inodeb), &next_block,
                                          &next_offset))
        goto failed_read;

    squashfs_new_inode(msblk, i, inodeb, ino);

    switch(inodeb->inode_type) {
    case SQUASHFS_FILE_TYPE: {
        struct squashfs_reg_inode_header_2 *inodep = &id.reg;
        struct squashfs_reg_inode_header_2 *sinodep = &sid.reg;
        long long frag_blk;
        unsigned int frag_size = 0;

        if (msblk->swap) {
            if (!squashfs_get_cached_block(s, (char *)
                                           sinodep, block, offset,
                                           sizeof(*sinodep), &next_block,
                                           &next_offset))
                goto failed_read;
            SQUASHFS_SWAP_REG_INODE_HEADER_2(inodep, sinodep);
        } else if (!squashfs_get_cached_block(s, (char *)
                                              inodep, block, offset,
                                              sizeof(*inodep), &next_block,
                                              &next_offset))
            goto failed_read;

        frag_blk = SQUASHFS_INVALID_BLK;
        if (inodep->fragment != SQUASHFS_INVALID_FRAG &&
                !get_fragment_location_2(s,
                                         inodep->fragment, &frag_blk, &frag_size))
            goto failed_read;

        i->i_size = inodep->file_size;
        i->i_fop = &generic_ro_fops;
        i->i_mode |= S_IFREG;
        i->i_mtime.tv_sec = inodep->mtime;
        i->i_atime.tv_sec = inodep->mtime;
        i->i_ctime.tv_sec = inodep->mtime;
        i->i_blocks = ((i->i_size - 1) >> 9) + 1;
        SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk;
        SQUASHFS_I(i)->u.s1.fragment_size = frag_size;
        SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset;
        SQUASHFS_I(i)->start_block = inodep->start_block;
        SQUASHFS_I(i)->u.s1.block_list_start = next_block;
        SQUASHFS_I(i)->offset = next_offset;
        if (sblk->block_size > 4096)
            i->i_data.a_ops = &squashfs_aops;
        else
            i->i_data.a_ops = &squashfs_aops_4K;

        TRACE("File inode %x:%x, start_block %x, "
              "block_list_start %llx, offset %x\n",
              SQUASHFS_INODE_BLK(inode), offset,
              inodep->start_block, next_block,
              next_offset);
        break;
    }
    case SQUASHFS_DIR_TYPE: {
        struct squashfs_dir_inode_header_2 *inodep = &id.dir;
        struct squashfs_dir_inode_header_2 *sinodep = &sid.dir;

        if (msblk->swap) {
            if (!squashfs_get_cached_block(s, (char *)
                                           sinodep, block, offset,
                                           sizeof(*sinodep), &next_block,
                                           &next_offset))
                goto failed_read;
            SQUASHFS_SWAP_DIR_INODE_HEADER_2(inodep, sinodep);
        } else if (!squashfs_get_cached_block(s, (char *)
                                              inodep, block, offset,
                                              sizeof(*inodep), &next_block,
                                              &next_offset))
            goto failed_read;

        i->i_size = inodep->file_size;
        i->i_op = &squashfs_dir_inode_ops_2;
        i->i_fop = &squashfs_dir_ops_2;
        i->i_mode |= S_IFDIR;
        i->i_mtime.tv_sec = inodep->mtime;
        i->i_atime.tv_sec = inodep->mtime;
        i->i_ctime.tv_sec = inodep->mtime;
        SQUASHFS_I(i)->start_block = inodep->start_block;
        SQUASHFS_I(i)->offset = inodep->offset;
        SQUASHFS_I(i)->u.s2.directory_index_count = 0;
        SQUASHFS_I(i)->u.s2.parent_inode = 0;

        TRACE("Directory inode %x:%x, start_block %x, offset "
              "%x\n", SQUASHFS_INODE_BLK(inode),
              offset, inodep->start_block,
              inodep->offset);
        break;
    }
    case SQUASHFS_LDIR_TYPE: {
        struct squashfs_ldir_inode_header_2 *inodep = &id.ldir;
        struct squashfs_ldir_inode_header_2 *sinodep = &sid.ldir;

        if (msblk->swap) {
            if (!squashfs_get_cached_block(s, (char *)
                                           sinodep, block, offset,
                                           sizeof(*sinodep), &next_block,
                                           &next_offset))
                goto failed_read;
            SQUASHFS_SWAP_LDIR_INODE_HEADER_2(inodep,
                                              sinodep);
        } else if (!squashfs_get_cached_block(s, (char *)
                                              inodep, block, offset,
                                              sizeof(*inodep), &next_block,
                                              &next_offset))
            goto failed_read;

        i->i_size = inodep->file_size;
        i->i_op = &squashfs_dir_inode_ops_2;
        i->i_fop = &squashfs_dir_ops_2;
        i->i_mode |= S_IFDIR;
        i->i_mtime.tv_sec = inodep->mtime;
        i->i_atime.tv_sec = inodep->mtime;
        i->i_ctime.tv_sec = inodep->mtime;
        SQUASHFS_I(i)->start_block = inodep->start_block;
        SQUASHFS_I(i)->offset = inodep->offset;
        SQUASHFS_I(i)->u.s2.directory_index_start = next_block;
        SQUASHFS_I(i)->u.s2.directory_index_offset =
            next_offset;
        SQUASHFS_I(i)->u.s2.directory_index_count =
            inodep->i_count;
        SQUASHFS_I(i)->u.s2.parent_inode = 0;

        TRACE("Long directory inode %x:%x, start_block %x, "
              "offset %x\n",
              SQUASHFS_INODE_BLK(inode), offset,
              inodep->start_block, inodep->offset);
        break;
    }
    case SQUASHFS_SYMLINK_TYPE: {
        struct squashfs_symlink_inode_header_2 *inodep =
                &id.symlink;
        struct squashfs_symlink_inode_header_2 *sinodep =
                &sid.symlink;

        if (msblk->swap) {
            if (!squashfs_get_cached_block(s, (char *)
                                           sinodep, block, offset,
                                           sizeof(*sinodep), &next_block,
                                           &next_offset))
                goto failed_read;
            SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(inodep,
                                                 sinodep);
        } else if (!squashfs_get_cached_block(s, (char *)
                                              inodep, block, offset,
                                              sizeof(*inodep), &next_block,
                                              &next_offset))
            goto failed_read;

        i->i_size = inodep->symlink_size;
        i->i_op = &page_symlink_inode_operations;
        i->i_data.a_ops = &squashfs_symlink_aops;
        i->i_mode |= S_IFLNK;
        SQUASHFS_I(i)->start_block = next_block;
        SQUASHFS_I(i)->offset = next_offset;

        TRACE("Symbolic link inode %x:%x, start_block %llx, "
              "offset %x\n",
              SQUASHFS_INODE_BLK(inode), offset,
              next_block, next_offset);
        break;
    }
    case SQUASHFS_BLKDEV_TYPE:
    case SQUASHFS_CHRDEV_TYPE: {
        struct squashfs_dev_inode_header_2 *inodep = &id.dev;
        struct squashfs_dev_inode_header_2 *sinodep = &sid.dev;

        if (msblk->swap) {
            if (!squashfs_get_cached_block(s, (char *)
                                           sinodep, block, offset,
                                           sizeof(*sinodep), &next_block,
                                           &next_offset))
                goto failed_read;
            SQUASHFS_SWAP_DEV_INODE_HEADER_2(inodep, sinodep);
        } else if (!squashfs_get_cached_block(s, (char *)
                                              inodep, block, offset,
                                              sizeof(*inodep), &next_block,
                                              &next_offset))
            goto failed_read;

        i->i_mode |= (inodeb->inode_type ==
                      SQUASHFS_CHRDEV_TYPE) ?  S_IFCHR :
                     S_IFBLK;
        init_special_inode(i, i->i_mode,
                           old_decode_dev(inodep->rdev));

        TRACE("Device inode %x:%x, rdev %x\n",
              SQUASHFS_INODE_BLK(inode), offset,
              inodep->rdev);
        break;
    }
    case SQUASHFS_FIFO_TYPE:
    case SQUASHFS_SOCKET_TYPE: {

        i->i_mode |= (inodeb->inode_type == SQUASHFS_FIFO_TYPE)
                     ? S_IFIFO : S_IFSOCK;
        init_special_inode(i, i->i_mode, 0);
        break;
    }
    default:
        ERROR("Unknown inode type %d in squashfs_iget!\n",
              inodeb->inode_type);
        goto failed_read1;
    }

    return 1;

failed_read:
    ERROR("Unable to read inode [%x:%x]\n", block, offset);

failed_read1:
    return 0;
}