diff --git a/fs_mgr/liblp/images.cpp b/fs_mgr/liblp/images.cpp index 98d57f699..742b1d0c1 100644 --- a/fs_mgr/liblp/images.cpp +++ b/fs_mgr/liblp/images.cpp @@ -41,6 +41,23 @@ std::unique_ptr ReadFromImageFile(int fd) { return ParseMetadata(geometry, fd); } +std::unique_ptr ReadFromImageBlob(const void* data, size_t bytes) { + if (bytes < LP_METADATA_GEOMETRY_SIZE) { + LERROR << __PRETTY_FUNCTION__ << ": " << bytes << " is smaller than geometry header"; + return nullptr; + } + + LpMetadataGeometry geometry; + if (!ParseGeometry(data, &geometry)) { + return nullptr; + } + + const uint8_t* metadata_buffer = + reinterpret_cast(data) + LP_METADATA_GEOMETRY_SIZE; + size_t metadata_buffer_size = bytes - LP_METADATA_GEOMETRY_SIZE; + return ParseMetadata(geometry, metadata_buffer, metadata_buffer_size); +} + std::unique_ptr ReadFromImageFile(const char* file) { android::base::unique_fd fd(open(file, O_RDONLY)); if (fd < 0) { diff --git a/fs_mgr/liblp/include/liblp/liblp.h b/fs_mgr/liblp/include/liblp/liblp.h index dd9a9cdf6..6da24f6ac 100644 --- a/fs_mgr/liblp/include/liblp/liblp.h +++ b/fs_mgr/liblp/include/liblp/liblp.h @@ -63,6 +63,7 @@ bool WriteToSparseFile(const char* file, const LpMetadata& metadata, uint32_t bl const std::map& images); bool WriteToImageFile(const char* file, const LpMetadata& metadata); std::unique_ptr ReadFromImageFile(const char* file); +std::unique_ptr ReadFromImageBlob(const void* data, size_t bytes); // Helper to extract safe C++ strings from partition info. std::string GetPartitionName(const LpMetadataPartition& partition); diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp index a8d6d7051..329a9016a 100644 --- a/fs_mgr/liblp/io_test.cpp +++ b/fs_mgr/liblp/io_test.cpp @@ -394,6 +394,27 @@ TEST(liblp, ImageFiles) { ASSERT_NE(imported, nullptr); } +// Test that we can read images from buffers. +TEST(liblp, ImageFilesInMemory) { + unique_ptr builder = CreateDefaultBuilder(); + ASSERT_NE(builder, nullptr); + ASSERT_TRUE(AddDefaultPartitions(builder.get())); + unique_ptr exported = builder->Export(); + + unique_fd fd(syscall(__NR_memfd_create, "image_file", 0)); + ASSERT_GE(fd, 0); + ASSERT_TRUE(WriteToImageFile(fd, *exported.get())); + + int64_t offset = SeekFile64(fd, 0, SEEK_CUR); + ASSERT_GE(offset, 0); + ASSERT_EQ(SeekFile64(fd, 0, SEEK_SET), 0); + + size_t bytes = static_cast(offset); + std::unique_ptr buffer = std::make_unique(bytes); + ASSERT_TRUE(android::base::ReadFully(fd, buffer.get(), bytes)); + ASSERT_NE(ReadFromImageBlob(buffer.get(), bytes), nullptr); +} + class BadWriter { public: // When requested, write garbage instead of the requested bytes, then diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp index d57af3fb0..117f5d5e6 100644 --- a/fs_mgr/liblp/reader.cpp +++ b/fs_mgr/liblp/reader.cpp @@ -48,9 +48,27 @@ class FileReader final : public Reader { int fd_; }; -// Parse an LpMetadataGeometry from a buffer. The buffer must be at least -// LP_METADATA_GEOMETRY_SIZE bytes in size. -static bool ParseGeometry(const void* buffer, LpMetadataGeometry* geometry) { +class MemoryReader final : public Reader { + public: + MemoryReader(const void* buffer, size_t size) + : buffer_(reinterpret_cast(buffer)), size_(size), pos_(0) {} + bool ReadFully(void* out, size_t length) override { + if (size_ - pos_ < length) { + errno = EINVAL; + return false; + } + memcpy(out, buffer_ + pos_, length); + pos_ += length; + return true; + } + + private: + const uint8_t* buffer_; + size_t size_; + size_t pos_; +}; + +bool ParseGeometry(const void* buffer, LpMetadataGeometry* geometry) { static_assert(sizeof(*geometry) <= LP_METADATA_GEOMETRY_SIZE); memcpy(geometry, buffer, sizeof(*geometry)); @@ -254,6 +272,12 @@ static std::unique_ptr ParseMetadata(const LpMetadataGeometry& geome return metadata; } +std::unique_ptr ParseMetadata(const LpMetadataGeometry& geometry, const void* buffer, + size_t size) { + MemoryReader reader(buffer, size); + return ParseMetadata(geometry, &reader); +} + std::unique_ptr ParseMetadata(const LpMetadataGeometry& geometry, int fd) { FileReader reader(fd); return ParseMetadata(geometry, &reader); diff --git a/fs_mgr/liblp/reader.h b/fs_mgr/liblp/reader.h index ae0875bb8..9f6ca6e98 100644 --- a/fs_mgr/liblp/reader.h +++ b/fs_mgr/liblp/reader.h @@ -26,11 +26,16 @@ namespace android { namespace fs_mgr { -std::unique_ptr ReadMetadata(int fd, uint32_t slot_number); +// Parse an LpMetadataGeometry from a buffer. The buffer must be at least +// LP_METADATA_GEOMETRY_SIZE bytes in size. +bool ParseGeometry(const void* buffer, LpMetadataGeometry* geometry); // Helper functions for manually reading geometry and metadata. -bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry); +std::unique_ptr ReadMetadata(int fd, uint32_t slot_number); std::unique_ptr ParseMetadata(const LpMetadataGeometry& geometry, int fd); +std::unique_ptr ParseMetadata(const LpMetadataGeometry& geometry, const void* buffer, + size_t size); +bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry); // These functions assume a valid geometry and slot number. std::unique_ptr ReadPrimaryMetadata(int fd, const LpMetadataGeometry& geometry,