1 //
2 // Copyright (C) 2020 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 "snapshot_reader.h"
18
19 #include <android-base/file.h>
20 #include <android-base/logging.h>
21
22 namespace android {
23 namespace snapshot {
24
25 using android::base::borrowed_fd;
26
CompressedSnapshotReader(std::unique_ptr<ICowReader> && cow,const std::optional<std::string> & source_device,std::optional<uint64_t> block_dev_size)27 CompressedSnapshotReader::CompressedSnapshotReader(std::unique_ptr<ICowReader>&& cow,
28 const std::optional<std::string>& source_device,
29 std::optional<uint64_t> block_dev_size)
30 : cow_(std::move(cow)),
31 block_size_(cow_->GetHeader().block_size),
32 source_device_(source_device),
33 block_device_size_(block_dev_size.value_or(0)) {
34 const auto& header = cow_->GetHeader();
35 block_size_ = header.block_size;
36
37 // Populate the operation map.
38 op_iter_ = cow_->GetOpIter(false);
39 while (!op_iter_->AtEnd()) {
40 const CowOperation* op = op_iter_->Get();
41 if (IsMetadataOp(*op)) {
42 op_iter_->Next();
43 continue;
44 }
45
46 size_t num_blocks = 1;
47 if (op->type() == kCowReplaceOp) {
48 num_blocks = (CowOpCompressionSize(op, block_size_) / block_size_);
49 }
50 if (op->new_block >= ops_.size()) {
51 ops_.resize(op->new_block + num_blocks, nullptr);
52 }
53
54 size_t vec_index = op->new_block;
55 while (num_blocks) {
56 ops_[vec_index] = op;
57 num_blocks -= 1;
58 vec_index += 1;
59 }
60 op_iter_->Next();
61 }
62 }
63
64 // Not supported.
Open(const char *,int,mode_t)65 bool CompressedSnapshotReader::Open(const char*, int, mode_t) {
66 errno = EINVAL;
67 return false;
68 }
69
Open(const char *,int)70 bool CompressedSnapshotReader::Open(const char*, int) {
71 errno = EINVAL;
72 return false;
73 }
74
Write(const void *,size_t)75 ssize_t CompressedSnapshotReader::Write(const void*, size_t) {
76 errno = EINVAL;
77 return false;
78 }
79
BlkIoctl(int,uint64_t,uint64_t,int *)80 bool CompressedSnapshotReader::BlkIoctl(int, uint64_t, uint64_t, int*) {
81 errno = EINVAL;
82 return false;
83 }
84
GetSourceFd()85 borrowed_fd CompressedSnapshotReader::GetSourceFd() {
86 if (source_fd_ < 0) {
87 if (!source_device_) {
88 LOG(ERROR) << "CompressedSnapshotReader needs source device, but none was set";
89 errno = EINVAL;
90 return {-1};
91 }
92 source_fd_.reset(open(source_device_->c_str(), O_RDONLY | O_CLOEXEC));
93 if (source_fd_ < 0) {
94 PLOG(ERROR) << "open " << *source_device_;
95 return {-1};
96 }
97 }
98 return source_fd_;
99 }
100
Read(void * buf,size_t count)101 ssize_t CompressedSnapshotReader::Read(void* buf, size_t count) {
102 // Find the start and end chunks, inclusive.
103 uint64_t start_chunk = offset_ / block_size_;
104 uint64_t end_chunk = (offset_ + count - 1) / block_size_;
105
106 // Chop off the first N bytes if the position is not block-aligned.
107 size_t start_offset = offset_ % block_size_;
108
109 uint8_t* buf_pos = reinterpret_cast<uint8_t*>(buf);
110 size_t buf_remaining = count;
111
112 size_t initial_bytes = std::min(block_size_ - start_offset, buf_remaining);
113 ssize_t rv = ReadBlock(start_chunk, start_offset, buf_pos, initial_bytes);
114 if (rv < 0) {
115 return -1;
116 }
117 offset_ += rv;
118 buf_pos += rv;
119 buf_remaining -= rv;
120
121 for (uint64_t chunk = start_chunk + 1; chunk < end_chunk; chunk++) {
122 ssize_t rv = ReadBlock(chunk, 0, buf_pos, buf_remaining);
123 if (rv < 0) {
124 return -1;
125 }
126 offset_ += rv;
127 buf_pos += rv;
128 buf_remaining -= rv;
129 }
130
131 if (buf_remaining) {
132 ssize_t rv = ReadBlock(end_chunk, 0, buf_pos, buf_remaining);
133 if (rv < 0) {
134 return -1;
135 }
136 offset_ += rv;
137 buf_pos += rv;
138 buf_remaining -= rv;
139 }
140
141 CHECK_EQ(buf_pos - reinterpret_cast<uint8_t*>(buf), count);
142 CHECK_EQ(buf_remaining, 0);
143
144 errno = 0;
145 return count;
146 }
147
ReadBlock(uint64_t chunk,size_t start_offset,void * buffer,size_t buffer_size)148 ssize_t CompressedSnapshotReader::ReadBlock(uint64_t chunk, size_t start_offset, void* buffer,
149 size_t buffer_size) {
150 size_t bytes_to_read = std::min(static_cast<size_t>(block_size_), buffer_size);
151
152 // The offset is relative to the chunk; we should be reading no more than
153 // one chunk.
154 CHECK(start_offset + bytes_to_read <= block_size_);
155
156 const CowOperation* op = nullptr;
157 if (chunk < ops_.size()) {
158 op = ops_[chunk];
159 }
160
161 if (!op || op->type() == kCowCopyOp) {
162 borrowed_fd fd = GetSourceFd();
163 if (fd < 0) {
164 // GetSourceFd sets errno.
165 return -1;
166 }
167
168 if (op) {
169 uint64_t source_offset;
170 if (!cow_->GetSourceOffset(op, &source_offset)) {
171 LOG(ERROR) << "GetSourceOffset failed in CompressedSnapshotReader for op: " << *op;
172 return false;
173 }
174 chunk = GetBlockFromOffset(cow_->GetHeader(), source_offset);
175 }
176
177 off64_t offset = (chunk * block_size_) + start_offset;
178 if (!android::base::ReadFullyAtOffset(fd, buffer, bytes_to_read, offset)) {
179 PLOG(ERROR) << "read " << *source_device_;
180 // ReadFullyAtOffset sets errno.
181 return -1;
182 }
183 } else if (op->type() == kCowZeroOp) {
184 memset(buffer, 0, bytes_to_read);
185 } else if (op->type() == kCowReplaceOp) {
186 size_t buffer_size = CowOpCompressionSize(op, block_size_);
187 uint8_t temp_buffer[buffer_size];
188 if (cow_->ReadData(op, temp_buffer, buffer_size, 0) < buffer_size) {
189 LOG(ERROR) << "CompressedSnapshotReader failed to read replace op: buffer_size: "
190 << buffer_size << "start_offset: " << start_offset;
191 errno = EIO;
192 return -1;
193 }
194 off_t block_offset{};
195 if (!GetBlockOffset(op, chunk, block_size_, &block_offset)) {
196 LOG(ERROR) << "GetBlockOffset failed";
197 return -1;
198 }
199 std::memcpy(buffer, (char*)temp_buffer + block_offset + start_offset, bytes_to_read);
200 } else if (op->type() == kCowXorOp) {
201 borrowed_fd fd = GetSourceFd();
202 if (fd < 0) {
203 // GetSourceFd sets errno.
204 return -1;
205 }
206
207 uint64_t source_offset;
208 if (!cow_->GetSourceOffset(op, &source_offset)) {
209 LOG(ERROR) << "GetSourceOffset failed in CompressedSnapshotReader for op: " << *op;
210 return false;
211 }
212 off64_t offset = source_offset + start_offset;
213
214 std::string data(bytes_to_read, '\0');
215 if (!android::base::ReadFullyAtOffset(fd, data.data(), data.size(), offset)) {
216 PLOG(ERROR) << "read " << *source_device_;
217 // ReadFullyAtOffset sets errno.
218 return -1;
219 }
220
221 if (cow_->ReadData(op, buffer, bytes_to_read, start_offset) < bytes_to_read) {
222 LOG(ERROR) << "CompressedSnapshotReader failed to read xor op";
223 errno = EIO;
224 return -1;
225 }
226
227 for (size_t i = 0; i < bytes_to_read; i++) {
228 ((char*)buffer)[i] ^= data[i];
229 }
230 } else {
231 LOG(ERROR) << "CompressedSnapshotReader unknown op type: " << uint32_t(op->type());
232 errno = EINVAL;
233 return -1;
234 }
235
236 // MemoryByteSink doesn't do anything in ReturnBuffer, so don't bother calling it.
237 return bytes_to_read;
238 }
239
Seek(off64_t offset,int whence)240 off64_t CompressedSnapshotReader::Seek(off64_t offset, int whence) {
241 switch (whence) {
242 case SEEK_SET:
243 offset_ = offset;
244 break;
245 case SEEK_END:
246 offset_ = static_cast<off64_t>(block_device_size_) + offset;
247 break;
248 case SEEK_CUR:
249 offset_ += offset;
250 break;
251 default:
252 LOG(ERROR) << "Unrecognized seek whence: " << whence;
253 errno = EINVAL;
254 return -1;
255 }
256 return offset_;
257 }
258
BlockDevSize()259 uint64_t CompressedSnapshotReader::BlockDevSize() {
260 return block_device_size_;
261 }
262
Close()263 bool CompressedSnapshotReader::Close() {
264 cow_ = nullptr;
265 source_fd_ = {};
266 return true;
267 }
268
IsSettingErrno()269 bool CompressedSnapshotReader::IsSettingErrno() {
270 return true;
271 }
272
IsOpen()273 bool CompressedSnapshotReader::IsOpen() {
274 return cow_ != nullptr;
275 }
276
Flush()277 bool CompressedSnapshotReader::Flush() {
278 return true;
279 }
280
281 } // namespace snapshot
282 } // namespace android
283