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 #include "parser_v3.h"
15 
16 #include <android-base/file.h>
17 #include <android-base/logging.h>
18 #include <android-base/strings.h>
19 
20 #include <libsnapshot/cow_format.h>
21 #include <libsnapshot/cow_reader.h>
22 
23 namespace android {
24 namespace snapshot {
25 
26 using android::base::borrowed_fd;
27 
Parse(borrowed_fd fd,const CowHeaderV3 & header,std::optional<uint64_t> label)28 bool CowParserV3::Parse(borrowed_fd fd, const CowHeaderV3& header, std::optional<uint64_t> label) {
29     auto pos = lseek(fd.get(), 0, SEEK_END);
30     if (pos < 0) {
31         PLOG(ERROR) << "lseek end failed";
32         return false;
33     }
34     fd_size_ = pos;
35     header_ = header;
36 
37     if (header_.footer_size != 0) {
38         LOG(ERROR) << "Footer size isn't 0, read " << header_.footer_size;
39         return false;
40     }
41 
42     if (header_.op_size != sizeof(CowOperationV3)) {
43         LOG(ERROR) << "Operation size unknown, read " << header_.op_size << ", expected "
44                    << sizeof(CowOperationV3);
45         return false;
46     }
47     if (header_.cluster_ops != 0) {
48         LOG(ERROR) << "Cluster ops not supported in v3";
49         return false;
50     }
51 
52     if (header_.prefix.major_version != 3 || header_.prefix.minor_version != 0) {
53         LOG(ERROR) << "Header version mismatch, "
54                    << "major version: " << header_.prefix.major_version
55                    << ", expected: " << kCowVersionMajor
56                    << ", minor version: " << header_.prefix.minor_version
57                    << ", expected: " << kCowVersionMinor;
58         return false;
59     }
60 
61     std::optional<uint32_t> op_index = header_.op_count;
62     if (label) {
63         if (!ReadResumeBuffer(fd)) {
64             PLOG(ERROR) << "Failed to read resume buffer";
65             return false;
66         }
67         op_index = FindResumeOp(label.value());
68         if (op_index == std::nullopt) {
69             LOG(ERROR) << "failed to get op index from given label: " << label.value();
70             return false;
71         }
72     }
73 
74     return ParseOps(fd, op_index.value());
75 }
76 
ReadResumeBuffer(borrowed_fd fd)77 bool CowParserV3::ReadResumeBuffer(borrowed_fd fd) {
78     resume_points_ = std::make_shared<std::vector<ResumePoint>>(header_.resume_point_count);
79 
80     return android::base::ReadFullyAtOffset(fd, resume_points_->data(),
81                                             header_.resume_point_count * sizeof(ResumePoint),
82                                             GetResumeOffset(header_));
83 }
84 
FindResumeOp(const uint64_t label)85 std::optional<uint32_t> CowParserV3::FindResumeOp(const uint64_t label) {
86     for (auto& resume_point : *resume_points_) {
87         if (resume_point.label == label) {
88             return resume_point.op_index;
89         }
90     }
91     LOG(ERROR) << "failed to find label: " << label << "from following labels";
92     LOG(ERROR) << android::base::Join(*resume_points_, " ");
93 
94     return std::nullopt;
95 }
96 
ParseOps(borrowed_fd fd,const uint32_t op_index)97 bool CowParserV3::ParseOps(borrowed_fd fd, const uint32_t op_index) {
98     ops_ = std::make_shared<std::vector<CowOperationV3>>();
99     ops_->resize(op_index);
100 
101     // read beginning of operation buffer -> so op_index = 0
102     const off_t offset = GetOpOffset(0, header_);
103     if (!android::base::ReadFullyAtOffset(fd, ops_->data(), ops_->size() * sizeof(CowOperationV3),
104                                           offset)) {
105         PLOG(ERROR) << "read ops failed";
106         return false;
107     }
108 
109     // fill out mapping of XOR op data location
110     uint64_t data_pos = GetDataOffset(header_);
111 
112     xor_data_loc_ = std::make_shared<std::unordered_map<uint64_t, uint64_t>>();
113 
114     for (auto op : *ops_) {
115         if (op.type() == kCowXorOp) {
116             xor_data_loc_->insert({op.new_block, data_pos});
117         } else if (op.type() == kCowReplaceOp) {
118             if (data_pos != op.source()) {
119                 LOG(ERROR) << "Invalid data location for operation " << op
120                            << ", expected: " << data_pos;
121                 return false;
122             }
123         }
124         data_pos += op.data_length;
125     }
126     // :TODO: sequence buffer & resume buffer follow
127     // Once we implement labels, we'll have to discard unused ops and adjust
128     // the header as needed.
129 
130     ops_->shrink_to_fit();
131 
132     return true;
133 }
134 
Translate(TranslatedCowOps * out)135 bool CowParserV3::Translate(TranslatedCowOps* out) {
136     out->ops = ops_;
137     out->header = header_;
138     return true;
139 }
140 
141 }  // namespace snapshot
142 }  // namespace android
143