Merge "Return bytes written in CowWriter" am: b745a960f8
am: 9bfc688fe9
am: c0728eb19f
am: e5562054e6
Original change: https://android-review.googlesource.com/c/platform/system/core/+/1419388 Change-Id: Ifebc992c4c4903ff2c7bd5a991b43653b1f7551c
This commit is contained in:
commit
1a347cddad
3 changed files with 71 additions and 9 deletions
|
@ -12,11 +12,15 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <libsnapshot/cow_reader.h>
|
||||
#include <libsnapshot/cow_writer.h>
|
||||
|
@ -235,6 +239,34 @@ TEST_F(CowTest, HorribleSink) {
|
|||
ASSERT_EQ(sink.stream(), data);
|
||||
}
|
||||
|
||||
TEST_F(CowTest, GetSize) {
|
||||
CowOptions options;
|
||||
CowWriter writer(options);
|
||||
if (ftruncate(cow_->fd, 0) < 0) {
|
||||
perror("Fails to set temp file size");
|
||||
FAIL();
|
||||
}
|
||||
ASSERT_TRUE(writer.Initialize(cow_->fd));
|
||||
|
||||
std::string data = "This is some data, believe it";
|
||||
data.resize(options.block_size, '\0');
|
||||
|
||||
ASSERT_TRUE(writer.AddCopy(10, 20));
|
||||
ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
|
||||
ASSERT_TRUE(writer.AddZeroBlocks(51, 2));
|
||||
auto size_before = writer.GetCowSize();
|
||||
ASSERT_TRUE(writer.Finalize());
|
||||
auto size_after = writer.GetCowSize();
|
||||
ASSERT_EQ(size_before, size_after);
|
||||
struct stat buf;
|
||||
|
||||
if (fstat(cow_->fd, &buf) < 0) {
|
||||
perror("Fails to determine size of cow image written");
|
||||
FAIL();
|
||||
}
|
||||
ASSERT_EQ(buf.st_size, writer.GetCowSize());
|
||||
}
|
||||
|
||||
} // namespace snapshot
|
||||
} // namespace android
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <libsnapshot/cow_writer.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <zlib.h>
|
||||
|
@ -70,7 +71,7 @@ bool CowWriter::Initialize(android::base::borrowed_fd fd) {
|
|||
|
||||
// Headers are not complete, but this ensures the file is at the right
|
||||
// position.
|
||||
if (!android::base::WriteFully(fd_, &header_, sizeof(header_))) {
|
||||
if (!WriteFully(fd_, &header_, sizeof(header_))) {
|
||||
PLOG(ERROR) << "write failed";
|
||||
return false;
|
||||
}
|
||||
|
@ -120,7 +121,7 @@ bool CowWriter::AddRawBlocks(uint64_t new_block_start, const void* data, size_t
|
|||
LOG(ERROR) << "Compressed block is too large: " << data.size() << " bytes";
|
||||
return false;
|
||||
}
|
||||
if (!android::base::WriteFully(fd_, data.data(), data.size())) {
|
||||
if (!WriteFully(fd_, data.data(), data.size())) {
|
||||
PLOG(ERROR) << "AddRawBlocks: write failed";
|
||||
return false;
|
||||
}
|
||||
|
@ -136,7 +137,7 @@ bool CowWriter::AddRawBlocks(uint64_t new_block_start, const void* data, size_t
|
|||
iter += header_.block_size;
|
||||
}
|
||||
|
||||
if (!compression_ && !android::base::WriteFully(fd_, data, size)) {
|
||||
if (!compression_ && !WriteFully(fd_, data, size)) {
|
||||
PLOG(ERROR) << "AddRawBlocks: write failed";
|
||||
return false;
|
||||
}
|
||||
|
@ -186,6 +187,10 @@ static void SHA256(const void* data, size_t length, uint8_t out[32]) {
|
|||
}
|
||||
|
||||
bool CowWriter::Finalize() {
|
||||
// If both fields are set then Finalize is already called.
|
||||
if (header_.ops_offset > 0 && header_.ops_size > 0) {
|
||||
return true;
|
||||
}
|
||||
auto offs = lseek(fd_.get(), 0, SEEK_CUR);
|
||||
if (offs < 0) {
|
||||
PLOG(ERROR) << "lseek failed";
|
||||
|
@ -197,10 +202,12 @@ bool CowWriter::Finalize() {
|
|||
SHA256(ops_.data(), ops_.size(), header_.ops_checksum);
|
||||
SHA256(&header_, sizeof(header_), header_.header_checksum);
|
||||
|
||||
if (lseek(fd_.get(), 0, SEEK_SET) < 0) {
|
||||
PLOG(ERROR) << "lseek start failed";
|
||||
if (lseek(fd_.get(), 0, SEEK_SET)) {
|
||||
PLOG(ERROR) << "lseek failed";
|
||||
return false;
|
||||
}
|
||||
// Header is already written, calling WriteFully will increment
|
||||
// bytes_written_. So use android::base::WriteFully() here.
|
||||
if (!android::base::WriteFully(fd_, &header_, sizeof(header_))) {
|
||||
PLOG(ERROR) << "write header failed";
|
||||
return false;
|
||||
|
@ -209,13 +216,20 @@ bool CowWriter::Finalize() {
|
|||
PLOG(ERROR) << "lseek ops failed";
|
||||
return false;
|
||||
}
|
||||
if (!android::base::WriteFully(fd_, ops_.data(), ops_.size())) {
|
||||
if (!WriteFully(fd_, ops_.data(), ops_.size())) {
|
||||
PLOG(ERROR) << "write ops failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
// clear ops_ so that subsequent calls to GetSize() still works.
|
||||
ops_.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t CowWriter::GetCowSize() {
|
||||
return bytes_written_ + ops_.size() * sizeof(ops_[0]);
|
||||
}
|
||||
|
||||
bool CowWriter::GetDataPos(uint64_t* pos) {
|
||||
off_t offs = lseek(fd_.get(), 0, SEEK_CUR);
|
||||
if (offs < 0) {
|
||||
|
@ -226,5 +240,10 @@ bool CowWriter::GetDataPos(uint64_t* pos) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool CowWriter::WriteFully(base::borrowed_fd fd, const void* data, size_t size) {
|
||||
bytes_written_ += size;
|
||||
return android::base::WriteFully(fd, data, size);
|
||||
}
|
||||
|
||||
} // namespace snapshot
|
||||
} // namespace android
|
||||
|
|
|
@ -47,6 +47,14 @@ class ICowWriter {
|
|||
// Encode a sequence of zeroed blocks. |size| must be a multiple of the block size.
|
||||
virtual bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) = 0;
|
||||
|
||||
// Finalize all COW operations and flush pending writes.
|
||||
// Return true if successful.
|
||||
virtual bool Finalize() = 0;
|
||||
|
||||
// Return 0 if failed, on success return number of bytes the cow image would be
|
||||
// after calling Finalize();
|
||||
virtual size_t GetCowSize() = 0;
|
||||
|
||||
protected:
|
||||
CowOptions options_;
|
||||
};
|
||||
|
@ -63,23 +71,26 @@ class CowWriter : public ICowWriter {
|
|||
bool AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
|
||||
bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
|
||||
|
||||
// Finalize all COW operations and flush pending writes.
|
||||
bool Finalize();
|
||||
bool Finalize() override;
|
||||
|
||||
size_t GetCowSize() override;
|
||||
|
||||
private:
|
||||
void SetupHeaders();
|
||||
bool GetDataPos(uint64_t* pos);
|
||||
bool WriteFully(base::borrowed_fd fd, const void* data, size_t size);
|
||||
std::basic_string<uint8_t> Compress(const void* data, size_t length);
|
||||
|
||||
private:
|
||||
android::base::unique_fd owned_fd_;
|
||||
android::base::borrowed_fd fd_;
|
||||
CowHeader header_;
|
||||
CowHeader header_{};
|
||||
int compression_ = 0;
|
||||
|
||||
// :TODO: this is not efficient, but stringstream ubsan aborts because some
|
||||
// bytes overflow a signed char.
|
||||
std::basic_string<uint8_t> ops_;
|
||||
std::atomic<size_t> bytes_written_ = 0;
|
||||
};
|
||||
|
||||
} // namespace snapshot
|
||||
|
|
Loading…
Reference in a new issue