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