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