1 //
2 // Copyright (C) 2021 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #include "update_engine/payload_consumer/cow_writer_file_descriptor.h"
18
19 #include <memory>
20 #include <utility>
21
22 #include <base/logging.h>
23
24 #include "update_engine/common/utils.h"
25 #include "update_engine/payload_consumer/file_descriptor.h"
26
27 namespace chromeos_update_engine {
CowWriterFileDescriptor(std::unique_ptr<android::snapshot::ISnapshotWriter> cow_writer)28 CowWriterFileDescriptor::CowWriterFileDescriptor(
29 std::unique_ptr<android::snapshot::ISnapshotWriter> cow_writer)
30 : cow_writer_(std::move(cow_writer)),
31 cow_reader_(cow_writer_->OpenReader()) {
32 CHECK_NE(cow_writer_, nullptr);
33 CHECK_NE(cow_reader_, nullptr);
34 }
35
Open(const char * path,int flags,mode_t mode)36 bool CowWriterFileDescriptor::Open(const char* path, int flags, mode_t mode) {
37 LOG(ERROR) << "CowWriterFileDescriptor doesn't support Open()";
38 return false;
39 }
Open(const char * path,int flags)40 bool CowWriterFileDescriptor::Open(const char* path, int flags) {
41 LOG(ERROR) << "CowWriterFileDescriptor doesn't support Open()";
42 return false;
43 }
44
Read(void * buf,size_t count)45 ssize_t CowWriterFileDescriptor::Read(void* buf, size_t count) {
46 if (dirty_) {
47 // OK, CowReader provides a snapshot view of what the cow contains. Which
48 // means any writes happened after opening a CowReader isn't visible to
49 // that CowReader. Therefore, we re-open CowReader whenever we attempt a
50 // read after write. This does incur an overhead everytime you read after
51 // write.
52 // The usage of |dirty_| flag to coordinate re-open is a very coarse grained
53 // checked. This implementation has suboptimal performance. For better
54 // performance, keep track of blocks which are overwritten, and only re-open
55 // if reading a dirty block.
56 // TODO(b/173432386) Implement finer grained dirty checks
57 const auto offset = cow_reader_->Seek(0, SEEK_CUR);
58 cow_reader_.reset();
59 if (!cow_writer_->Finalize()) {
60 LOG(ERROR) << "Failed to Finalize() cow writer";
61 return -1;
62 }
63 cow_reader_ = cow_writer_->OpenReader();
64 if (cow_reader_ == nullptr) {
65 LOG(ERROR) << "Failed to re-open cow reader after writing to COW";
66 return -1;
67 }
68 const auto pos = cow_reader_->Seek(offset, SEEK_SET);
69 if (pos != offset) {
70 LOG(ERROR) << "Failed to seek to previous position after re-opening cow "
71 "reader, expected "
72 << offset << " actual: " << pos;
73 return -1;
74 }
75 dirty_ = false;
76 }
77 return cow_reader_->Read(buf, count);
78 }
79
Write(const void * buf,size_t count)80 ssize_t CowWriterFileDescriptor::Write(const void* buf, size_t count) {
81 auto offset = cow_reader_->Seek(0, SEEK_CUR);
82 CHECK_EQ(offset % cow_writer_->options().block_size, 0);
83 auto success = cow_writer_->AddRawBlocks(
84 offset / cow_writer_->options().block_size, buf, count);
85 if (success) {
86 if (cow_reader_->Seek(count, SEEK_CUR) < 0) {
87 return -1;
88 }
89 dirty_ = true;
90 return count;
91 }
92 return -1;
93 }
94
Seek(const off64_t offset,int whence)95 off64_t CowWriterFileDescriptor::Seek(const off64_t offset, int whence) {
96 return cow_reader_->Seek(offset, whence);
97 }
98
BlockDevSize()99 uint64_t CowWriterFileDescriptor::BlockDevSize() {
100 LOG(ERROR) << "CowWriterFileDescriptor doesn't support BlockDevSize()";
101 return 0;
102 }
103
BlkIoctl(int request,uint64_t start,uint64_t length,int * result)104 bool CowWriterFileDescriptor::BlkIoctl(int request,
105 uint64_t start,
106 uint64_t length,
107 int* result) {
108 LOG(ERROR) << "CowWriterFileDescriptor doesn't support BlkIoctl()";
109 return false;
110 }
111
Flush()112 bool CowWriterFileDescriptor::Flush() {
113 // CowWriter already automatilly flushes, no need to do anything.
114 return true;
115 }
116
Close()117 bool CowWriterFileDescriptor::Close() {
118 if (cow_writer_) {
119 // b/186196758
120 // When calling
121 // InitializeAppend(kEndOfInstall), the SnapshotWriter only reads up to the
122 // given label. But OpenReader() completely disregards the resume label and
123 // reads all ops. Therefore, update_engine sees the verity data. However,
124 // when calling SnapshotWriter::Finalize(), data after resume label are
125 // discarded, therefore verity data is gone. To prevent phantom reads, don't
126 // call Finalize() unless we actually write something.
127 if (dirty_) {
128 TEST_AND_RETURN_FALSE(cow_writer_->Finalize());
129 }
130 cow_writer_ = nullptr;
131 }
132 if (cow_reader_) {
133 TEST_AND_RETURN_FALSE(cow_reader_->Close());
134 cow_reader_ = nullptr;
135 }
136 return true;
137 }
138
IsSettingErrno()139 bool CowWriterFileDescriptor::IsSettingErrno() {
140 return false;
141 }
142
IsOpen()143 bool CowWriterFileDescriptor::IsOpen() {
144 return cow_writer_ != nullptr && cow_reader_ != nullptr;
145 }
146
~CowWriterFileDescriptor()147 CowWriterFileDescriptor::~CowWriterFileDescriptor() {
148 Close();
149 }
150
151 } // namespace chromeos_update_engine
152