sqfs_inode.c 3.8 KB

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