[fvm] [host] Use SparseReader to read sparse files
Test: fvm-test
Change-Id: I4eeaa1e1085f5391ec2ead6a29f8c31f0a5e5f32
diff --git a/system/host/fvm/container/sparse.cpp b/system/host/fvm/container/sparse.cpp
index 0b46813..e55fd3c 100644
--- a/system/host/fvm/container/sparse.cpp
+++ b/system/host/fvm/container/sparse.cpp
@@ -102,34 +102,31 @@
if (s.st_size > 0) {
disk_size_ = s.st_size;
- if (read(fd_.get(), &image_, sizeof(fvm::sparse_image_t)) != sizeof(fvm::sparse_image_t)) {
- fprintf(stderr, "SparseContainer: Failed to read the sparse header\n");
+ fbl::unique_fd dup_fd(dup(fd_.get()));
+ if (fvm::SparseReader::CreateSilent(std::move(dup_fd), &reader_) != ZX_OK) {
+ fprintf(stderr, "SparseContainer: Failed to read metadata from sparse file\n");
return;
}
- if (image_.flags & fvm::kSparseFlagLz4) {
- return;
- }
+ memcpy(&image_, reader_->Image(), sizeof(fvm::sparse_image_t));
extent_size_ = disk_size_ - image_.header_length;
+ uintptr_t partition_ptr = reinterpret_cast<uintptr_t>(reader_->Partitions());
+
for (unsigned i = 0; i < image_.partition_count; i++) {
partition_info_t partition;
+ memcpy(&partition.descriptor, reinterpret_cast<void*>(partition_ptr),
+ sizeof(fvm::partition_descriptor_t));
partitions_.push_back(std::move(partition));
- if (read(fd_.get(), &partitions_[i].descriptor, sizeof(fvm::partition_descriptor_t)) !=
- sizeof(fvm::partition_descriptor_t)) {
- fprintf(stderr, "SparseContainer: Failed to read partition %u\n", i);
- return;
- }
+ partition_ptr += sizeof(fvm::partition_descriptor_t);
- for (unsigned j = 0; j < partitions_[i].descriptor.extent_count; j++) {
+ for (size_t j = 0; j < partitions_[i].descriptor.extent_count; j++) {
fvm::extent_descriptor_t extent;
+ memcpy(&extent, reinterpret_cast<void*>(partition_ptr),
+ sizeof(fvm::extent_descriptor_t));
partitions_[i].extents.push_back(extent);
- if (read(fd_.get(), &partitions_[i].extents[j], sizeof(fvm::extent_descriptor_t)) !=
- sizeof(fvm::extent_descriptor_t)) {
- fprintf(stderr, "SparseContainer: Failed to read extent\n");
- return;
- }
+ partition_ptr += sizeof(fvm::extent_descriptor_t);
}
}
@@ -160,6 +157,15 @@
fprintf(stderr, "SparseContainer: Found invalid container\n");
return ZX_ERR_INTERNAL;
}
+
+ if (image_.flags & fvm::kSparseFlagLz4) {
+ // Decompression must occur before verification, since all contents must be available for
+ // fsck.
+ fprintf(stderr, "SparseContainer: Found compressed container; contents cannot be"
+ " verified\n");
+ return ZX_ERR_INVALID_ARGS;
+ }
+
if (image_.magic != fvm::kSparseFormatMagic) {
fprintf(stderr, "SparseContainer: Bad magic\n");
return ZX_ERR_IO;
@@ -330,6 +336,23 @@
return ZX_OK;
}
+zx_status_t SparseContainer::Decompress(const char* path) {
+ if ((flags_ & fvm::kSparseFlagLz4) == 0) {
+ fprintf(stderr, "Cannot decompress un-compressed sparse file\n");
+ return ZX_ERR_NOT_SUPPORTED;
+ }
+
+ fbl::unique_fd fd;
+
+ fd.reset(open(path, O_WRONLY | O_CREAT | O_EXCL, 0644));
+ if (!fd) {
+ fprintf(stderr, "could not open %s: %s\n", path, strerror(errno));
+ return ZX_ERR_IO;
+ }
+
+ return reader_->WriteDecompressed(std::move(fd));
+}
+
zx_status_t SparseContainer::AllocatePartition(fbl::unique_ptr<Format> format) {
partition_info_t partition;
format->GetPartitionInfo(&partition.descriptor);
diff --git a/system/host/fvm/include/fvm/container.h b/system/host/fvm/include/fvm/container.h
index 6165ad8..199b1c5 100644
--- a/system/host/fvm/include/fvm/container.h
+++ b/system/host/fvm/include/fvm/container.h
@@ -10,6 +10,7 @@
#include <fbl/string_buffer.h>
#include <fbl/vector.h>
#include <fbl/unique_fd.h>
+#include <fvm/fvm-lz4.h>
#include <fvm/fvm-sparse.h>
#include "format.h"
@@ -211,6 +212,10 @@
size_t SliceSize() const final;
zx_status_t AddPartition(const char* path, const char* type_name) final;
+ // Decompresses the contents of the sparse file (if they are compressed), and writes the output
+ // to |path|.
+ zx_status_t Decompress(const char* path);
+
private:
bool valid_;
bool dirty_;
@@ -219,6 +224,7 @@
fvm::sparse_image_t image_;
fbl::Vector<partition_info_t> partitions_;
CompressionContext compression_;
+ fbl::unique_ptr<fvm::SparseReader> reader_;
zx_status_t AllocatePartition(fbl::unique_ptr<Format> format);
zx_status_t AllocateExtent(uint32_t part_index, uint64_t slice_start, uint64_t slice_count,
diff --git a/system/host/fvm/main.cpp b/system/host/fvm/main.cpp
index e26ce82..29d5e8a 100644
--- a/system/host/fvm/main.cpp
+++ b/system/host/fvm/main.cpp
@@ -23,7 +23,7 @@
" required)\n");
fprintf(stderr, " sparse : Creates a sparse file. One or more input paths are required.\n");
fprintf(stderr, " verify : Report basic information about sparse/fvm files and run fsck on"
- " contained partitions\n");
+ " contained partitions.\n");
fprintf(stderr, " decompress : Decompresses a compressed sparse file. --sparse input path is"
" required.\n");
fprintf(stderr, "Flags (neither or both of offset/length must be specified):\n");
@@ -265,12 +265,13 @@
return -1;
}
- if (fvm::decompress_sparse(input_path, path) != ZX_OK) {
+ SparseContainer compressedContainer(input_path, slice_size, flags);
+ if (compressedContainer.Decompress(path) != ZX_OK) {
return -1;
}
- fbl::unique_ptr<SparseContainer> sparseData(new SparseContainer(path, slice_size, flags));
- if (sparseData->Verify() != ZX_OK) {
+ SparseContainer sparseContainer(path, slice_size, flags);
+ if (sparseContainer.Verify() != ZX_OK) {
return -1;
}
} else {
diff --git a/system/ulib/fvm/fvm-lz4.cpp b/system/ulib/fvm/fvm-lz4.cpp
index f47dbae..2cf176b 100644
--- a/system/ulib/fvm/fvm-lz4.cpp
+++ b/system/ulib/fvm/fvm-lz4.cpp
@@ -8,8 +8,16 @@
namespace fvm {
zx_status_t SparseReader::Create(fbl::unique_fd fd, fbl::unique_ptr<SparseReader>* out) {
+ return SparseReader::CreateHelper(std::move(fd), true /* verbose */, out);
+}
+zx_status_t SparseReader::CreateSilent(fbl::unique_fd fd, fbl::unique_ptr<SparseReader>* out) {
+ return SparseReader::CreateHelper(std::move(fd), false /* verbose */, out);
+}
+
+zx_status_t SparseReader::CreateHelper(fbl::unique_fd fd, bool verbose,
+ fbl::unique_ptr<SparseReader>* out) {
fbl::AllocChecker ac;
- fbl::unique_ptr<SparseReader> reader(new (&ac) SparseReader(std::move(fd)));
+ fbl::unique_ptr<SparseReader> reader(new (&ac) SparseReader(std::move(fd), verbose));
if (!ac.check()) {
return ZX_ERR_NO_MEMORY;
}
@@ -23,7 +31,8 @@
return ZX_OK;
}
-SparseReader::SparseReader(fbl::unique_fd fd) : compressed_(false), fd_(std::move(fd)) {}
+SparseReader::SparseReader(fbl::unique_fd fd, bool verbose) : compressed_(false), verbose_(verbose),
+ fd_(std::move(fd)) {}
zx_status_t SparseReader::ReadMetadata() {
// Read sparse image header.
@@ -63,7 +72,10 @@
// If image is compressed, additional setup is required
if (image.flags & fvm::kSparseFlagLz4) {
- printf("Found compressed file\n");
+ if (verbose_) {
+ printf("Found compressed file\n");
+ }
+
compressed_ = true;
// Initialize decompression context
LZ4F_errorCode_t errc = LZ4F_createDecompressionContext(&dctx_, LZ4F_VERSION);
@@ -293,39 +305,17 @@
}
void SparseReader::PrintStats() const {
- printf("Reading FVM from compressed file: %s\n", compressed_ ? "true" : "false");
- printf("Remaining bytes read into compression buffer: %lu\n", in_buf_.size);
- printf("Remaining bytes written to decompression buffer: %lu\n", out_buf_.size);
+ if (verbose_) {
+ printf("Reading FVM from compressed file: %s\n", compressed_ ? "true" : "false");
+ printf("Remaining bytes read into compression buffer: %lu\n", in_buf_.size);
+ printf("Remaining bytes written to decompression buffer: %lu\n", out_buf_.size);
#ifdef __Fuchsia__
- printf("Time reading bytes from sparse FVM file: %lu (%lu s)\n", read_time_,
- read_time_ / zx_ticks_per_second());
- printf("Time reading bytes AND decompressing them: %lu (%lu s)\n", total_time_,
- total_time_ / zx_ticks_per_second());
+ printf("Time reading bytes from sparse FVM file: %lu (%lu s)\n", read_time_,
+ read_time_ / zx_ticks_per_second());
+ printf("Time reading bytes AND decompressing them: %lu (%lu s)\n", total_time_,
+ total_time_ / zx_ticks_per_second());
#endif
-}
-
-zx_status_t decompress_sparse(const char* infile, const char* outfile) {
- fbl::unique_fd infd, outfd, testfd;
-
- infd.reset(open(infile, O_RDONLY));
- if (!infd) {
- fprintf(stderr, "could not open %s: %s\n", infile, strerror(errno));
- return ZX_ERR_IO;
}
-
- outfd.reset(open(outfile, O_WRONLY | O_CREAT | O_EXCL, 0644));
- if (!outfd) {
- fprintf(stderr, "could not open %s: %s\n", outfile, strerror(errno));
- return ZX_ERR_IO;
- }
-
- zx_status_t status;
- fbl::unique_ptr<SparseReader> reader;
- if ((status = SparseReader::Create(std::move(infd), &reader))) {
- return status;
- }
-
- return reader->WriteDecompressed(std::move(outfd));
}
} // namespace fvm
diff --git a/system/ulib/fvm/include/fvm/fvm-lz4.h b/system/ulib/fvm/include/fvm/fvm-lz4.h
index 9d4a607..be8476f 100644
--- a/system/ulib/fvm/include/fvm/fvm-lz4.h
+++ b/system/ulib/fvm/include/fvm/fvm-lz4.h
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#pragma once
+
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
@@ -30,6 +32,7 @@
class SparseReader {
public:
static zx_status_t Create(fbl::unique_fd fd, fbl::unique_ptr<SparseReader>* out);
+ static zx_status_t CreateSilent(fbl::unique_fd fd, fbl::unique_ptr<SparseReader>* out);
~SparseReader();
fvm::sparse_image_t* Image();
@@ -42,7 +45,7 @@
private:
typedef struct buffer {
// Write |length| bytes from |indata| into buffer.
- bool is_empty() {
+ bool is_empty() const {
return offset == 0 && size == 0;
}
@@ -85,7 +88,10 @@
size_t max_size;
} buffer_t;
- SparseReader(fbl::unique_fd fd);
+ static zx_status_t CreateHelper(fbl::unique_fd fd, bool verbose,
+ fbl::unique_ptr<SparseReader>* out);
+
+ SparseReader(fbl::unique_fd fd, bool verbose);
// Read in header data, prepare buffers and decompression context if necessary
zx_status_t ReadMetadata();
// Initialize buffer with a given |size|
@@ -98,6 +104,9 @@
// True if sparse file is compressed
bool compressed_;
+ // If true, all logs are printed.
+ bool verbose_;
+
fbl::unique_fd fd_;
fbl::unique_ptr<uint8_t[]> metadata_;
LZ4F_decompressionContext_t dctx_;
@@ -119,6 +128,4 @@
#endif
};
-// Read from compressed |infile|, decompress, and write to |outfile|.
-zx_status_t decompress_sparse(const char* infile, const char* outfile);
} // namespace fvm
diff --git a/system/ulib/minfs/minfs.cpp b/system/ulib/minfs/minfs.cpp
index 1c81489..7506eb3 100644
--- a/system/ulib/minfs/minfs.cpp
+++ b/system/ulib/minfs/minfs.cpp
@@ -1173,6 +1173,11 @@
zx_status_t SparseFsck(fbl::unique_fd fd, off_t start, off_t end,
const fbl::Vector<size_t>& extent_lengths) {
+ if (start >= end) {
+ fprintf(stderr, "error: Insufficient space allocated\n");
+ return ZX_ERR_INVALID_ARGS;
+ }
+
if (extent_lengths.size() != kExtentCount) {
FS_TRACE_ERROR("error: invalid number of extents\n");
return ZX_ERR_INVALID_ARGS;
diff --git a/system/utest/fvm-host/main.cpp b/system/utest/fvm-host/main.cpp
index e0e64ba..40fbba3 100644
--- a/system/utest/fvm-host/main.cpp
+++ b/system/utest/fvm-host/main.cpp
@@ -181,23 +181,26 @@
}
bool ReportContainer(const char* path, off_t offset) {
+ BEGIN_HELPER;
fbl::unique_ptr<Container> container;
off_t length;
ASSERT_TRUE(StatFile(path, &length));
ASSERT_EQ(Container::Create(path, offset, length - offset, 0, &container), ZX_OK,
"Failed to initialize container");
ASSERT_EQ(container->Verify(), ZX_OK, "File check failed\n");
- return true;
+ END_HELPER;
}
bool ReportSparse(uint32_t flags) {
+ BEGIN_HELPER;
if ((flags & fvm::kSparseFlagLz4) != 0) {
unittest_printf("Decompressing sparse file\n");
- if (fvm::decompress_sparse(sparse_lz4_path, sparse_path) != ZX_OK) {
- return false;
- }
+ SparseContainer compressedContainer(sparse_lz4_path, DEFAULT_SLICE_SIZE, flags);
+ ASSERT_EQ(compressedContainer.Decompress(sparse_path), ZX_OK);
}
- return ReportContainer(sparse_path, 0);
+
+ ASSERT_TRUE(ReportContainer(sparse_path, 0));
+ END_HELPER;
}
bool CreateFvm(bool create_before, off_t offset, size_t slice_size) {