uboot/fs/squashfs/sqfs_inode.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (C) 2020 Bootlin
   4 *
   5 * Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
   6 */
   7
   8#include <asm/unaligned.h>
   9#include <compiler.h>
  10#include <errno.h>
  11#include <stdint.h>
  12#include <stdio.h>
  13#include <stdlib.h>
  14#include <string.h>
  15
  16#include "sqfs_decompressor.h"
  17#include "sqfs_filesystem.h"
  18#include "sqfs_utils.h"
  19
  20int sqfs_inode_size(struct squashfs_base_inode *inode, u32 blk_size)
  21{
  22        switch (get_unaligned_le16(&inode->inode_type)) {
  23        case SQFS_DIR_TYPE:
  24                return sizeof(struct squashfs_dir_inode);
  25
  26        case SQFS_REG_TYPE: {
  27                struct squashfs_reg_inode *reg =
  28                        (struct squashfs_reg_inode *)inode;
  29                u32 fragment = get_unaligned_le32(&reg->fragment);
  30                u32 file_size = get_unaligned_le32(&reg->file_size);
  31                unsigned int blk_list_size;
  32
  33                if (SQFS_IS_FRAGMENTED(fragment))
  34                        blk_list_size = file_size / blk_size;
  35                else
  36                        blk_list_size = DIV_ROUND_UP(file_size, blk_size);
  37
  38                return sizeof(*reg) + blk_list_size * sizeof(u32);
  39        }
  40
  41        case SQFS_LDIR_TYPE: {
  42                struct squashfs_ldir_inode *ldir =
  43                        (struct squashfs_ldir_inode *)inode;
  44                u16 i_count = get_unaligned_le16(&ldir->i_count);
  45                unsigned int index_list_size = 0, l = 0;
  46                struct squashfs_directory_index *di;
  47                u32 sz;
  48
  49                if (i_count == 0)
  50                        return sizeof(*ldir);
  51
  52                di = ldir->index;
  53                while (l < i_count) {
  54                        sz = get_unaligned_le32(&di->size) + 1;
  55                        index_list_size += sz;
  56                        di = (void *)di + sizeof(*di) + sz;
  57                        l++;
  58                }
  59
  60                return sizeof(*ldir) + index_list_size +
  61                        i_count * SQFS_DIR_INDEX_BASE_LENGTH;
  62        }
  63
  64        case SQFS_LREG_TYPE: {
  65                struct squashfs_lreg_inode *lreg =
  66                        (struct squashfs_lreg_inode *)inode;
  67                u32 fragment = get_unaligned_le32(&lreg->fragment);
  68                u64 file_size = get_unaligned_le64(&lreg->file_size);
  69                unsigned int blk_list_size;
  70
  71                if (fragment == 0xFFFFFFFF)
  72                        blk_list_size = DIV_ROUND_UP(file_size, blk_size);
  73                else
  74                        blk_list_size = file_size / blk_size;
  75
  76                return sizeof(*lreg) + blk_list_size * sizeof(u32);
  77        }
  78
  79        case SQFS_SYMLINK_TYPE:
  80        case SQFS_LSYMLINK_TYPE: {
  81                struct squashfs_symlink_inode *symlink =
  82                        (struct squashfs_symlink_inode *)inode;
  83
  84                return sizeof(*symlink) +
  85                        get_unaligned_le32(&symlink->symlink_size);
  86        }
  87
  88        case SQFS_BLKDEV_TYPE:
  89        case SQFS_CHRDEV_TYPE:
  90                return sizeof(struct squashfs_dev_inode);
  91        case SQFS_LBLKDEV_TYPE:
  92        case SQFS_LCHRDEV_TYPE:
  93                return sizeof(struct squashfs_ldev_inode);
  94        case SQFS_FIFO_TYPE:
  95        case SQFS_SOCKET_TYPE:
  96                return sizeof(struct squashfs_ipc_inode);
  97        case SQFS_LFIFO_TYPE:
  98        case SQFS_LSOCKET_TYPE:
  99                return sizeof(struct squashfs_lipc_inode);
 100        default:
 101                printf("Error while searching inode: unknown type.\n");
 102                return -EINVAL;
 103        }
 104}
 105
 106/*
 107 * Given the uncompressed inode table, the inode to be found and the number of
 108 * inodes in the table, return inode position in case of success.
 109 */
 110void *sqfs_find_inode(void *inode_table, int inode_number, __le32 inode_count,
 111                      __le32 block_size)
 112{
 113        struct squashfs_base_inode *base;
 114        unsigned int offset = 0, k;
 115        int sz;
 116
 117        if (!inode_table) {
 118                printf("%s: Invalid pointer to inode table.\n", __func__);
 119                return NULL;
 120        }
 121
 122        for (k = 0; k < le32_to_cpu(inode_count); k++) {
 123                base = inode_table + offset;
 124                if (get_unaligned_le32(&base->inode_number) == inode_number)
 125                        return inode_table + offset;
 126
 127                sz = sqfs_inode_size(base, le32_to_cpu(block_size));
 128                if (sz < 0)
 129                        return NULL;
 130
 131                offset += sz;
 132        }
 133
 134        printf("Inode not found.\n");
 135
 136        return NULL;
 137}
 138
 139int sqfs_read_metablock(unsigned char *file_mapping, int offset,
 140                        bool *compressed, u32 *data_size)
 141{
 142        const unsigned char *data;
 143        u16 header;
 144
 145        if (!file_mapping)
 146                return -EFAULT;
 147        data = file_mapping + offset;
 148
 149        header = get_unaligned((u16 *)data);
 150        if (!header)
 151                return -EINVAL;
 152
 153        *compressed = SQFS_COMPRESSED_METADATA(header);
 154        *data_size = SQFS_METADATA_SIZE(header);
 155
 156        if (*data_size > SQFS_METADATA_BLOCK_SIZE) {
 157                printf("Invalid metatada block size: %d bytes.\n", *data_size);
 158                return -EINVAL;
 159        }
 160
 161        return 0;
 162}
 163