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 <string>
21 #include <utility>
22 
23 #include <base/logging.h>
24 
25 #include "update_engine/common/utils.h"
26 #include "update_engine/payload_consumer/file_descriptor.h"
27 
28 namespace chromeos_update_engine {
29 
CowWriterFileDescriptor(std::unique_ptr<android::snapshot::ICowWriter> cow_writer,std::unique_ptr<FileDescriptor> cow_reader,const std::optional<std::string> & source_device)30 CowWriterFileDescriptor::CowWriterFileDescriptor(
31     std::unique_ptr<android::snapshot::ICowWriter> cow_writer,
32     std::unique_ptr<FileDescriptor> cow_reader,
33     const std::optional<std::string>& source_device)
34     : cow_writer_(std::move(cow_writer)),
35       cow_reader_(std::move(cow_reader)),
36       source_device_(source_device) {
37   CHECK_NE(cow_writer_, nullptr);
38   CHECK_NE(cow_reader_, nullptr);
39 }
40 
Open(const char * path,int flags,mode_t mode)41 bool CowWriterFileDescriptor::Open(const char* path, int flags, mode_t mode) {
42   LOG(ERROR) << "CowWriterFileDescriptor doesn't support Open()";
43   return false;
44 }
Open(const char * path,int flags)45 bool CowWriterFileDescriptor::Open(const char* path, int flags) {
46   LOG(ERROR) << "CowWriterFileDescriptor doesn't support Open()";
47   return false;
48 }
49 
Read(void * buf,size_t count)50 ssize_t CowWriterFileDescriptor::Read(void* buf, size_t count) {
51   if (dirty_) {
52     // OK, CowReader provides a snapshot view of what the cow contains. Which
53     // means any writes happened after opening a CowReader isn't visible to
54     // that CowReader. Therefore, we re-open CowReader whenever we attempt a
55     // read after write. This does incur an overhead everytime you read after
56     // write.
57     // The usage of |dirty_| flag to coordinate re-open is a very coarse grained
58     // checked. This implementation has suboptimal performance. For better
59     // performance, keep track of blocks which are overwritten, and only re-open
60     // if reading a dirty block.
61     // TODO(b/173432386) Implement finer grained dirty checks
62     const auto offset = cow_reader_->Seek(0, SEEK_CUR);
63     cow_reader_.reset();
64     if (!cow_writer_->Finalize()) {
65       LOG(ERROR) << "Failed to Finalize() cow writer";
66       return -1;
67     }
68     cow_reader_ = cow_writer_->OpenFileDescriptor(source_device_);
69     if (cow_reader_ == nullptr) {
70       LOG(ERROR)
71           << "Failed to re-open cow file descriptor after writing to COW";
72       return -1;
73     }
74     const auto pos = cow_reader_->Seek(offset, SEEK_SET);
75     if (pos != offset) {
76       LOG(ERROR) << "Failed to seek to previous position after re-opening cow "
77                     "reader, expected "
78                  << offset << " actual: " << pos;
79       return -1;
80     }
81     dirty_ = false;
82   }
83   return cow_reader_->Read(buf, count);
84 }
85 
Write(const void * buf,size_t count)86 ssize_t CowWriterFileDescriptor::Write(const void* buf, size_t count) {
87   auto offset = cow_reader_->Seek(0, SEEK_CUR);
88   CHECK_EQ(offset % cow_writer_->GetBlockSize(), 0);
89   auto success = cow_writer_->AddRawBlocks(
90       offset / cow_writer_->GetBlockSize(), buf, count);
91   if (success) {
92     if (cow_reader_->Seek(count, SEEK_CUR) < 0) {
93       return -1;
94     }
95     dirty_ = true;
96     return count;
97   }
98   return -1;
99 }
100 
Seek(const off64_t offset,int whence)101 off64_t CowWriterFileDescriptor::Seek(const off64_t offset, int whence) {
102   return cow_reader_->Seek(offset, whence);
103 }
104 
BlockDevSize()105 uint64_t CowWriterFileDescriptor::BlockDevSize() {
106   LOG(ERROR) << "CowWriterFileDescriptor doesn't support BlockDevSize()";
107   return 0;
108 }
109 
BlkIoctl(int request,uint64_t start,uint64_t length,int * result)110 bool CowWriterFileDescriptor::BlkIoctl(int request,
111                                        uint64_t start,
112                                        uint64_t length,
113                                        int* result) {
114   LOG(ERROR) << "CowWriterFileDescriptor doesn't support BlkIoctl()";
115   return false;
116 }
117 
Flush()118 bool CowWriterFileDescriptor::Flush() {
119   // CowWriter already automatilly flushes, no need to do anything.
120   return true;
121 }
122 
Close()123 bool CowWriterFileDescriptor::Close() {
124   if (cow_writer_) {
125     // b/186196758
126     // When calling
127     // InitializeAppend(kEndOfInstall), the SnapshotWriter only reads up to the
128     // given label. But OpenReader() completely disregards the resume label and
129     // reads all ops. Therefore, update_engine sees the verity data. However,
130     // when calling SnapshotWriter::Finalize(), data after resume label are
131     // discarded, therefore verity data is gone. To prevent phantom reads, don't
132     // call Finalize() unless we actually write something.
133     if (dirty_) {
134       TEST_AND_RETURN_FALSE(cow_writer_->Finalize());
135     }
136     cow_writer_ = nullptr;
137   }
138   if (cow_reader_) {
139     TEST_AND_RETURN_FALSE(cow_reader_->Close());
140     cow_reader_ = nullptr;
141   }
142   return true;
143 }
144 
IsSettingErrno()145 bool CowWriterFileDescriptor::IsSettingErrno() {
146   return false;
147 }
148 
IsOpen()149 bool CowWriterFileDescriptor::IsOpen() {
150   return cow_writer_ != nullptr && cow_reader_ != nullptr;
151 }
152 
~CowWriterFileDescriptor()153 CowWriterFileDescriptor::~CowWriterFileDescriptor() {
154   Close();
155 }
156 
157 }  // namespace chromeos_update_engine
158