update_engine: Add support for parsing the size of a squashfs.
This patch extends the utils::GetFilesystemSize() function to support
squashfs (in addition to the existing ext3 support). The filesystem
type is detected automatically.
squashfs doesn't define a block size for the physical device that it
relies on. There's a definition of block size for the data before
compression that doesn't affect the block size used for the underlying
device. When creating the squashfs, it is by default padded to 4 KiB,
so we assume 4 KiB as the block size. This is also the minimum block
size required by verity to compute hashes in all supported archs.
BUG=chromium:430956
TEST=Unittest added.
Change-Id: Ia683a27c094e25952e8d665535dc6219c849bdd0
Reviewed-on: https://chromium-review.googlesource.com/228906
Reviewed-by: Gaurav Shah <gauravsh@chromium.org>
Commit-Queue: Alex Deymo <deymo@chromium.org>
Tested-by: Alex Deymo <deymo@chromium.org>
diff --git a/utils.cc b/utils.cc
index f0bf6f7..c6c5226 100644
--- a/utils.cc
+++ b/utils.cc
@@ -10,6 +10,7 @@
#include <dirent.h>
#include <elf.h>
#include <errno.h>
+#include <ext2fs/ext2fs.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
@@ -26,8 +27,8 @@
#include <vector>
#include <base/callback.h>
-#include <base/files/file_util.h>
#include <base/files/file_path.h>
+#include <base/files/file_util.h>
#include <base/files/scoped_file.h>
#include <base/logging.h>
#include <base/posix/eintr_wrapper.h>
@@ -666,44 +667,106 @@
int* out_block_size) {
TEST_AND_RETURN_FALSE(fd >= 0);
- // Determine the ext3 filesystem size by directly reading the block count and
- // block size information from the superblock. See include/linux/ext3_fs.h for
- // more details on the structure.
- ssize_t kBufferSize = 16 * sizeof(uint32_t);
- char buffer[kBufferSize];
- const int kSuperblockOffset = 1024;
- if (HANDLE_EINTR(pread(fd, buffer, kBufferSize, kSuperblockOffset)) !=
- kBufferSize) {
- PLOG(ERROR) << "Unable to determine file system size:";
+ // Determine the filesystem size by directly reading the block count and
+ // block size information from the superblock. Supported FS are ext3 and
+ // squashfs.
+
+ // Read from the fd only once and detect in memory. The first 2 KiB is enough
+ // to read the ext2 superblock (located at offset 1024) and the squashfs
+ // superblock (located at offset 0).
+ const ssize_t kBufferSize = 2048;
+
+ uint8_t buffer[kBufferSize];
+ if (HANDLE_EINTR(pread(fd, buffer, kBufferSize, 0)) != kBufferSize) {
+ PLOG(ERROR) << "Unable to read the file system header:";
return false;
}
- uint32_t block_count; // ext3_fs.h: ext3_super_block.s_blocks_count
- uint32_t log_block_size; // ext3_fs.h: ext3_super_block.s_log_block_size
- uint16_t magic; // ext3_fs.h: ext3_super_block.s_magic
- memcpy(&block_count, &buffer[1 * sizeof(int32_t)], sizeof(block_count));
- memcpy(&log_block_size, &buffer[6 * sizeof(int32_t)], sizeof(log_block_size));
- memcpy(&magic, &buffer[14 * sizeof(int32_t)], sizeof(magic));
+
+ if (GetSquashfs4Size(buffer, kBufferSize, out_block_count, out_block_size))
+ return true;
+ if (GetExt3Size(buffer, kBufferSize, out_block_count, out_block_size))
+ return true;
+
+ LOG(ERROR) << "Unable to determine file system type.";
+ return false;
+}
+
+bool GetExt3Size(const uint8_t* buffer, size_t buffer_size,
+ int* out_block_count,
+ int* out_block_size) {
+ // See include/linux/ext2_fs.h for more details on the structure. We obtain
+ // ext2 constants from ext2fs/ext2fs.h header but we don't link with the
+ // library.
+ if (buffer_size < SUPERBLOCK_OFFSET + SUPERBLOCK_SIZE)
+ return false;
+
+ const uint8_t* superblock = buffer + SUPERBLOCK_OFFSET;
+
+ // ext3_fs.h: ext3_super_block.s_blocks_count
+ uint32_t block_count =
+ *reinterpret_cast<const uint32_t*>(superblock + 1 * sizeof(int32_t));
+
+ // ext3_fs.h: ext3_super_block.s_log_block_size
+ uint32_t log_block_size =
+ *reinterpret_cast<const uint32_t*>(superblock + 6 * sizeof(int32_t));
+
+ // ext3_fs.h: ext3_super_block.s_magic
+ uint16_t magic =
+ *reinterpret_cast<const uint16_t*>(superblock + 14 * sizeof(int32_t));
+
block_count = le32toh(block_count);
- const int kExt3MinBlockLogSize = 10; // ext3_fs.h: EXT3_MIN_BLOCK_LOG_SIZE
- log_block_size = le32toh(log_block_size) + kExt3MinBlockLogSize;
+ log_block_size = le32toh(log_block_size) + EXT2_MIN_BLOCK_LOG_SIZE;
magic = le16toh(magic);
// Sanity check the parameters.
- const uint16_t kExt3SuperMagic = 0xef53; // ext3_fs.h: EXT3_SUPER_MAGIC
- TEST_AND_RETURN_FALSE(magic == kExt3SuperMagic);
- const int kExt3MinBlockSize = 1024; // ext3_fs.h: EXT3_MIN_BLOCK_SIZE
- const int kExt3MaxBlockSize = 4096; // ext3_fs.h: EXT3_MAX_BLOCK_SIZE
- int block_size = 1 << log_block_size;
- TEST_AND_RETURN_FALSE(block_size >= kExt3MinBlockSize &&
- block_size <= kExt3MaxBlockSize);
+ TEST_AND_RETURN_FALSE(magic == EXT2_SUPER_MAGIC);
+ TEST_AND_RETURN_FALSE(log_block_size >= EXT2_MIN_BLOCK_LOG_SIZE &&
+ log_block_size <= EXT2_MAX_BLOCK_LOG_SIZE);
TEST_AND_RETURN_FALSE(block_count > 0);
- if (out_block_count) {
+ if (out_block_count)
*out_block_count = block_count;
+ if (out_block_size)
+ *out_block_size = 1 << log_block_size;
+ return true;
+}
+
+bool GetSquashfs4Size(const unsigned char* buffer, size_t buffer_size,
+ int* out_block_count,
+ int* out_block_size) {
+ // See fs/squashfs/squashfs_fs.h for format details. We only support
+ // Squashfs 4.x little endian.
+
+ // sizeof(struct squashfs_super_block)
+ const size_t kSquashfsSuperBlockSize = 96;
+ if (buffer_size < kSquashfsSuperBlockSize)
+ return false;
+
+ // Check magic, squashfs_fs.h: SQUASHFS_MAGIC
+ if (memcmp(buffer, "hsqs", 4) != 0)
+ return false; // Only little endian is supported.
+
+ // squashfs_fs.h: struct squashfs_super_block.s_major
+ uint16_t s_major = *reinterpret_cast<const uint16_t*>(
+ buffer + 5 * sizeof(uint32_t) + 4 * sizeof(uint16_t));
+
+ if (s_major != 4) {
+ LOG(ERROR) << "Found unsupported squashfs major version " << s_major;
+ return false;
}
- if (out_block_size) {
+
+ // squashfs_fs.h: struct squashfs_super_block.bytes_used
+ uint64_t bytes_used = *reinterpret_cast<const int64_t*>(
+ buffer + 5 * sizeof(uint32_t) + 6 * sizeof(uint16_t) + sizeof(uint64_t));
+
+ const int block_size = 4096;
+
+ // The squashfs' bytes_used doesn't need to be aligned with the block boundary
+ // so we round up to the nearest blocksize.
+ if (out_block_count)
+ *out_block_count = (bytes_used + block_size - 1) / block_size;
+ if (out_block_size)
*out_block_size = block_size;
- }
return true;
}