1 // Copyright (C) 2023 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "writer_base.h"
16
17 #include <fcntl.h>
18 #include <linux/fs.h>
19 #include <sys/ioctl.h>
20 #include <sys/stat.h>
21 #include <unistd.h>
22
23 #include <android-base/logging.h>
24 #include "snapshot_reader.h"
25
26 // The info messages here are spammy, but as useful for update_engine. Disable
27 // them when running on the host.
28 #ifdef __ANDROID__
29 #define LOG_INFO LOG(INFO)
30 #else
31 #define LOG_INFO LOG(VERBOSE)
32 #endif
33
34 namespace android {
35 namespace snapshot {
36
37 using android::base::borrowed_fd;
38 using android::base::unique_fd;
39
40 namespace {
GetFdPath(borrowed_fd fd)41 std::string GetFdPath(borrowed_fd fd) {
42 const auto fd_path = "/proc/self/fd/" + std::to_string(fd.get());
43 std::string file_path(512, '\0');
44 const auto err = readlink(fd_path.c_str(), file_path.data(), file_path.size());
45 if (err <= 0) {
46 PLOG(ERROR) << "Failed to determine path for fd " << fd.get();
47 file_path.clear();
48 } else {
49 file_path.resize(err);
50 }
51 return file_path;
52 }
53 } // namespace
54
CowWriterBase(const CowOptions & options,unique_fd && fd)55 CowWriterBase::CowWriterBase(const CowOptions& options, unique_fd&& fd)
56 : options_(options), fd_(std::move(fd)) {}
57
InitFd()58 bool CowWriterBase::InitFd() {
59 if (fd_.get() < 0) {
60 fd_.reset(open("/dev/null", O_RDWR | O_CLOEXEC));
61 if (fd_ < 0) {
62 PLOG(ERROR) << "open /dev/null failed";
63 return false;
64 }
65 is_dev_null_ = true;
66 return true;
67 }
68
69 struct stat stat {};
70 if (fstat(fd_.get(), &stat) < 0) {
71 PLOG(ERROR) << "fstat failed";
72 return false;
73 }
74 const auto file_path = GetFdPath(fd_);
75 is_block_device_ = S_ISBLK(stat.st_mode);
76 if (is_block_device_) {
77 uint64_t size_in_bytes = 0;
78 if (ioctl(fd_.get(), BLKGETSIZE64, &size_in_bytes)) {
79 PLOG(ERROR) << "Failed to get total size for: " << fd_.get();
80 return false;
81 }
82 cow_image_size_ = size_in_bytes;
83 LOG_INFO << "COW image " << file_path << " has size " << size_in_bytes;
84 } else {
85 LOG_INFO << "COW image " << file_path
86 << " is not a block device, assuming unlimited space.";
87 }
88 return true;
89 }
90
AddCopy(uint64_t new_block,uint64_t old_block,uint64_t num_blocks)91 bool CowWriterBase::AddCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks) {
92 CHECK(num_blocks != 0);
93
94 for (size_t i = 0; i < num_blocks; i++) {
95 if (!ValidateNewBlock(new_block + i)) {
96 return false;
97 }
98 }
99
100 return EmitCopy(new_block, old_block, num_blocks);
101 }
102
AddRawBlocks(uint64_t new_block_start,const void * data,size_t size)103 bool CowWriterBase::AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
104 if (size % options_.block_size != 0) {
105 LOG(ERROR) << "AddRawBlocks: size " << size << " is not a multiple of "
106 << options_.block_size;
107 return false;
108 }
109
110 uint64_t num_blocks = size / options_.block_size;
111 uint64_t last_block = new_block_start + num_blocks - 1;
112 if (!ValidateNewBlock(last_block)) {
113 return false;
114 }
115 return EmitRawBlocks(new_block_start, data, size);
116 }
117
AddXorBlocks(uint32_t new_block_start,const void * data,size_t size,uint32_t old_block,uint16_t offset)118 bool CowWriterBase::AddXorBlocks(uint32_t new_block_start, const void* data, size_t size,
119 uint32_t old_block, uint16_t offset) {
120 if (size % options_.block_size != 0) {
121 LOG(ERROR) << "AddRawBlocks: size " << size << " is not a multiple of "
122 << options_.block_size;
123 return false;
124 }
125
126 uint64_t num_blocks = size / options_.block_size;
127 uint64_t last_block = new_block_start + num_blocks - 1;
128 if (!ValidateNewBlock(last_block)) {
129 return false;
130 }
131 if (offset >= options_.block_size) {
132 LOG(ERROR) << "AddXorBlocks: offset " << offset << " is not less than "
133 << options_.block_size;
134 }
135 return EmitXorBlocks(new_block_start, data, size, old_block, offset);
136 }
137
AddZeroBlocks(uint64_t new_block_start,uint64_t num_blocks)138 bool CowWriterBase::AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
139 uint64_t last_block = new_block_start + num_blocks - 1;
140 if (!ValidateNewBlock(last_block)) {
141 return false;
142 }
143 return EmitZeroBlocks(new_block_start, num_blocks);
144 }
145
AddLabel(uint64_t label)146 bool CowWriterBase::AddLabel(uint64_t label) {
147 return EmitLabel(label);
148 }
149
AddSequenceData(size_t num_ops,const uint32_t * data)150 bool CowWriterBase::AddSequenceData(size_t num_ops, const uint32_t* data) {
151 return EmitSequenceData(num_ops, data);
152 }
153
ValidateNewBlock(uint64_t new_block)154 bool CowWriterBase::ValidateNewBlock(uint64_t new_block) {
155 if (options_.max_blocks && new_block >= options_.max_blocks.value()) {
156 LOG(ERROR) << "New block " << new_block << " exceeds maximum block count "
157 << options_.max_blocks.value();
158 return false;
159 }
160 return true;
161 }
162
OpenReader()163 std::unique_ptr<ICowReader> CowWriterBase::OpenReader() {
164 unique_fd cow_fd(fcntl(fd_.get(), F_DUPFD | F_DUPFD_CLOEXEC, 0));
165 if (cow_fd < 0) {
166 PLOG(ERROR) << "CowWriterV2::OpenReander: dup COW device";
167 return nullptr;
168 }
169
170 auto cow = std::make_unique<CowReader>();
171 if (!cow->Parse(std::move(cow_fd))) {
172 LOG(ERROR) << "CowWriterV2::OpenReader: unable to read COW";
173 return nullptr;
174 }
175 return cow;
176 }
177
OpenFileDescriptor(const std::optional<std::string> & source_device)178 std::unique_ptr<chromeos_update_engine::FileDescriptor> CowWriterBase::OpenFileDescriptor(
179 const std::optional<std::string>& source_device) {
180 auto reader = OpenReader();
181 if (!reader) {
182 return nullptr;
183 }
184
185 std::optional<uint64_t> block_dev_size;
186 if (options_.max_blocks) {
187 block_dev_size = {*options_.max_blocks * options_.block_size};
188 }
189
190 return std::make_unique<CompressedSnapshotReader>(std::move(reader), source_device,
191 block_dev_size);
192 }
193
Sync()194 bool CowWriterBase::Sync() {
195 if (is_dev_null_) {
196 return true;
197 }
198 if (fsync(fd_.get()) < 0) {
199 PLOG(ERROR) << "fsync failed";
200 return false;
201 }
202 return true;
203 }
204
205 } // namespace snapshot
206 } // namespace android
207