1*5a923131SAndroid Build Coastguard Worker //
2*5a923131SAndroid Build Coastguard Worker // Copyright (C) 2021 The Android Open Source Project
3*5a923131SAndroid Build Coastguard Worker //
4*5a923131SAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
5*5a923131SAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
6*5a923131SAndroid Build Coastguard Worker // You may obtain a copy of the License at
7*5a923131SAndroid Build Coastguard Worker //
8*5a923131SAndroid Build Coastguard Worker // http://www.apache.org/licenses/LICENSE-2.0
9*5a923131SAndroid Build Coastguard Worker //
10*5a923131SAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
11*5a923131SAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
12*5a923131SAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*5a923131SAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
14*5a923131SAndroid Build Coastguard Worker // limitations under the License.
15*5a923131SAndroid Build Coastguard Worker //
16*5a923131SAndroid Build Coastguard Worker
17*5a923131SAndroid Build Coastguard Worker #include "lz4diff.h"
18*5a923131SAndroid Build Coastguard Worker #include "lz4diff/lz4patch.h"
19*5a923131SAndroid Build Coastguard Worker #include "lz4diff_compress.h"
20*5a923131SAndroid Build Coastguard Worker #include "update_engine/payload_generator/filesystem_interface.h"
21*5a923131SAndroid Build Coastguard Worker #include "update_engine/payload_generator/erofs_filesystem.h"
22*5a923131SAndroid Build Coastguard Worker #include "update_engine/common/utils.h"
23*5a923131SAndroid Build Coastguard Worker
24*5a923131SAndroid Build Coastguard Worker using namespace chromeos_update_engine;
25*5a923131SAndroid Build Coastguard Worker
26*5a923131SAndroid Build Coastguard Worker template <typename T>
operator <<(std::ostream & out,const std::vector<T> & vec)27*5a923131SAndroid Build Coastguard Worker std::ostream& operator<<(std::ostream& out, const std::vector<T>& vec) {
28*5a923131SAndroid Build Coastguard Worker if (vec.begin() == vec.end()) {
29*5a923131SAndroid Build Coastguard Worker out << "{}";
30*5a923131SAndroid Build Coastguard Worker return out;
31*5a923131SAndroid Build Coastguard Worker }
32*5a923131SAndroid Build Coastguard Worker out << "{";
33*5a923131SAndroid Build Coastguard Worker auto begin = vec.begin();
34*5a923131SAndroid Build Coastguard Worker out << *begin;
35*5a923131SAndroid Build Coastguard Worker for (const auto& ext : Range{++begin, vec.end()}) {
36*5a923131SAndroid Build Coastguard Worker out << ", " << ext;
37*5a923131SAndroid Build Coastguard Worker }
38*5a923131SAndroid Build Coastguard Worker out << "}";
39*5a923131SAndroid Build Coastguard Worker return out;
40*5a923131SAndroid Build Coastguard Worker }
41*5a923131SAndroid Build Coastguard Worker
42*5a923131SAndroid Build Coastguard Worker enum Lz4DiffOp { DIFF, PATCH, TEST };
43*5a923131SAndroid Build Coastguard Worker
ExecuteLz4diff(const char * src_image_path,const char * dst_image_path,const FilesystemInterface::File & src_file,const FilesystemInterface::File & dst_file,const char * patch_file,Lz4DiffOp op)44*5a923131SAndroid Build Coastguard Worker int ExecuteLz4diff(const char* src_image_path,
45*5a923131SAndroid Build Coastguard Worker const char* dst_image_path,
46*5a923131SAndroid Build Coastguard Worker const FilesystemInterface::File& src_file,
47*5a923131SAndroid Build Coastguard Worker const FilesystemInterface::File& dst_file,
48*5a923131SAndroid Build Coastguard Worker const char* patch_file,
49*5a923131SAndroid Build Coastguard Worker Lz4DiffOp op) {
50*5a923131SAndroid Build Coastguard Worker brillo::Blob src_blob;
51*5a923131SAndroid Build Coastguard Worker CHECK(utils::ReadExtents(
52*5a923131SAndroid Build Coastguard Worker src_image_path, src_file.extents, &src_blob, kBlockSize));
53*5a923131SAndroid Build Coastguard Worker brillo::Blob dst_blob;
54*5a923131SAndroid Build Coastguard Worker CHECK(utils::ReadExtents(
55*5a923131SAndroid Build Coastguard Worker dst_image_path, dst_file.extents, &dst_blob, kBlockSize));
56*5a923131SAndroid Build Coastguard Worker
57*5a923131SAndroid Build Coastguard Worker brillo::Blob lz4diff_patch;
58*5a923131SAndroid Build Coastguard Worker if (op == DIFF || op == TEST) {
59*5a923131SAndroid Build Coastguard Worker Lz4Diff(src_blob,
60*5a923131SAndroid Build Coastguard Worker dst_blob,
61*5a923131SAndroid Build Coastguard Worker src_file.compressed_file_info,
62*5a923131SAndroid Build Coastguard Worker dst_file.compressed_file_info,
63*5a923131SAndroid Build Coastguard Worker &lz4diff_patch);
64*5a923131SAndroid Build Coastguard Worker if (patch_file) {
65*5a923131SAndroid Build Coastguard Worker CHECK(utils::WriteFile(
66*5a923131SAndroid Build Coastguard Worker patch_file, lz4diff_patch.data(), lz4diff_patch.size()));
67*5a923131SAndroid Build Coastguard Worker }
68*5a923131SAndroid Build Coastguard Worker }
69*5a923131SAndroid Build Coastguard Worker if (op == PATCH || op == TEST) {
70*5a923131SAndroid Build Coastguard Worker utils::ReadFile(patch_file, &lz4diff_patch);
71*5a923131SAndroid Build Coastguard Worker Blob actual_target;
72*5a923131SAndroid Build Coastguard Worker CHECK(Lz4Patch(
73*5a923131SAndroid Build Coastguard Worker ToStringView(src_blob), ToStringView(lz4diff_patch), &actual_target));
74*5a923131SAndroid Build Coastguard Worker if (actual_target != dst_blob) {
75*5a923131SAndroid Build Coastguard Worker LOG(ERROR) << "Final postfixed blob mismatch. " << src_file.name;
76*5a923131SAndroid Build Coastguard Worker } else {
77*5a923131SAndroid Build Coastguard Worker LOG(INFO) << "LZ4patch success. Final blob matches. " << src_file.name;
78*5a923131SAndroid Build Coastguard Worker }
79*5a923131SAndroid Build Coastguard Worker }
80*5a923131SAndroid Build Coastguard Worker return 0;
81*5a923131SAndroid Build Coastguard Worker }
82*5a923131SAndroid Build Coastguard Worker
ExecuteLz4diffOp(const char * src_image_path,const char * dst_image_path,const char * inode_path,const char * patch_file,Lz4DiffOp op)83*5a923131SAndroid Build Coastguard Worker int ExecuteLz4diffOp(const char* src_image_path,
84*5a923131SAndroid Build Coastguard Worker const char* dst_image_path,
85*5a923131SAndroid Build Coastguard Worker const char* inode_path,
86*5a923131SAndroid Build Coastguard Worker const char* patch_file,
87*5a923131SAndroid Build Coastguard Worker Lz4DiffOp op) {
88*5a923131SAndroid Build Coastguard Worker auto src_fs = ErofsFilesystem::CreateFromFile(src_image_path);
89*5a923131SAndroid Build Coastguard Worker CHECK_NE(src_fs, nullptr);
90*5a923131SAndroid Build Coastguard Worker auto dst_fs = ErofsFilesystem::CreateFromFile(dst_image_path);
91*5a923131SAndroid Build Coastguard Worker CHECK_NE(dst_fs, nullptr);
92*5a923131SAndroid Build Coastguard Worker std::vector<FilesystemInterface::File> src_files;
93*5a923131SAndroid Build Coastguard Worker CHECK(src_fs->GetFiles(&src_files));
94*5a923131SAndroid Build Coastguard Worker std::vector<FilesystemInterface::File> dst_files;
95*5a923131SAndroid Build Coastguard Worker CHECK(dst_fs->GetFiles(&dst_files));
96*5a923131SAndroid Build Coastguard Worker ScopedTempFile temp_patch;
97*5a923131SAndroid Build Coastguard Worker if (patch_file == nullptr && op == TEST) {
98*5a923131SAndroid Build Coastguard Worker patch_file = temp_patch.path().c_str();
99*5a923131SAndroid Build Coastguard Worker }
100*5a923131SAndroid Build Coastguard Worker if (inode_path == nullptr) {
101*5a923131SAndroid Build Coastguard Worker for (const auto& src_file : src_files) {
102*5a923131SAndroid Build Coastguard Worker auto dst_file = std::find_if(
103*5a923131SAndroid Build Coastguard Worker dst_files.begin(),
104*5a923131SAndroid Build Coastguard Worker dst_files.end(),
105*5a923131SAndroid Build Coastguard Worker [path(src_file.name)](auto&& file) { return file.name == path; });
106*5a923131SAndroid Build Coastguard Worker int err = ExecuteLz4diff(
107*5a923131SAndroid Build Coastguard Worker src_image_path, dst_image_path, src_file, *dst_file, patch_file, op);
108*5a923131SAndroid Build Coastguard Worker if (err) {
109*5a923131SAndroid Build Coastguard Worker return err;
110*5a923131SAndroid Build Coastguard Worker }
111*5a923131SAndroid Build Coastguard Worker }
112*5a923131SAndroid Build Coastguard Worker return 0;
113*5a923131SAndroid Build Coastguard Worker }
114*5a923131SAndroid Build Coastguard Worker auto src_file = std::find_if(
115*5a923131SAndroid Build Coastguard Worker src_files.begin(), src_files.end(), [inode_path](auto&& file) {
116*5a923131SAndroid Build Coastguard Worker return file.name == inode_path;
117*5a923131SAndroid Build Coastguard Worker });
118*5a923131SAndroid Build Coastguard Worker if (src_file == src_files.end()) {
119*5a923131SAndroid Build Coastguard Worker LOG(ERROR) << "Failed to find " << inode_path << " in EROFS image"
120*5a923131SAndroid Build Coastguard Worker << src_image_path;
121*5a923131SAndroid Build Coastguard Worker return 2;
122*5a923131SAndroid Build Coastguard Worker }
123*5a923131SAndroid Build Coastguard Worker auto dst_file = std::find_if(
124*5a923131SAndroid Build Coastguard Worker dst_files.begin(), dst_files.end(), [inode_path](auto&& file) {
125*5a923131SAndroid Build Coastguard Worker return file.name == inode_path;
126*5a923131SAndroid Build Coastguard Worker });
127*5a923131SAndroid Build Coastguard Worker if (dst_file == dst_files.end()) {
128*5a923131SAndroid Build Coastguard Worker LOG(ERROR) << "Failed to find " << inode_path << " in EROFS image"
129*5a923131SAndroid Build Coastguard Worker << dst_image_path;
130*5a923131SAndroid Build Coastguard Worker return 3;
131*5a923131SAndroid Build Coastguard Worker }
132*5a923131SAndroid Build Coastguard Worker return ExecuteLz4diff(
133*5a923131SAndroid Build Coastguard Worker src_image_path, dst_image_path, *src_file, *dst_file, patch_file, op);
134*5a923131SAndroid Build Coastguard Worker }
135*5a923131SAndroid Build Coastguard Worker
main(int argc,const char ** argv)136*5a923131SAndroid Build Coastguard Worker int main(int argc, const char** argv) {
137*5a923131SAndroid Build Coastguard Worker if (argc < 4) {
138*5a923131SAndroid Build Coastguard Worker printf(
139*5a923131SAndroid Build Coastguard Worker "Usage: %s <diff/patch/test> <src EROFS image> <dst EROFS image> "
140*5a923131SAndroid Build Coastguard Worker "...args\n",
141*5a923131SAndroid Build Coastguard Worker argv[0]);
142*5a923131SAndroid Build Coastguard Worker return 2;
143*5a923131SAndroid Build Coastguard Worker }
144*5a923131SAndroid Build Coastguard Worker const char* src_image_path = argv[2];
145*5a923131SAndroid Build Coastguard Worker const char* dst_image_path = argv[3];
146*5a923131SAndroid Build Coastguard Worker auto src_fs = ErofsFilesystem::CreateFromFile(src_image_path);
147*5a923131SAndroid Build Coastguard Worker CHECK_NE(src_fs, nullptr);
148*5a923131SAndroid Build Coastguard Worker auto dst_fs = ErofsFilesystem::CreateFromFile(dst_image_path);
149*5a923131SAndroid Build Coastguard Worker CHECK_NE(dst_fs, nullptr);
150*5a923131SAndroid Build Coastguard Worker std::vector<FilesystemInterface::File> src_files;
151*5a923131SAndroid Build Coastguard Worker CHECK(src_fs->GetFiles(&src_files));
152*5a923131SAndroid Build Coastguard Worker std::vector<FilesystemInterface::File> dst_files;
153*5a923131SAndroid Build Coastguard Worker CHECK(dst_fs->GetFiles(&dst_files));
154*5a923131SAndroid Build Coastguard Worker std::string_view op = argv[1];
155*5a923131SAndroid Build Coastguard Worker if (op == "diff") {
156*5a923131SAndroid Build Coastguard Worker if (argc != 6 && argc != 5) {
157*5a923131SAndroid Build Coastguard Worker printf(
158*5a923131SAndroid Build Coastguard Worker "Usage: %s diff <path to src erofs image> <path to dst erofs imaeg> "
159*5a923131SAndroid Build Coastguard Worker "<path "
160*5a923131SAndroid Build Coastguard Worker "of file inside erofs image> [output path]\n",
161*5a923131SAndroid Build Coastguard Worker argv[0]);
162*5a923131SAndroid Build Coastguard Worker return 1;
163*5a923131SAndroid Build Coastguard Worker }
164*5a923131SAndroid Build Coastguard Worker const char* path = argv[4];
165*5a923131SAndroid Build Coastguard Worker const char* patch_file = argc == 6 ? argv[5] : nullptr;
166*5a923131SAndroid Build Coastguard Worker return ExecuteLz4diffOp(
167*5a923131SAndroid Build Coastguard Worker src_image_path, dst_image_path, path, patch_file, DIFF);
168*5a923131SAndroid Build Coastguard Worker } else if (op == "patch") {
169*5a923131SAndroid Build Coastguard Worker if (argc != 6 && argc != 7) {
170*5a923131SAndroid Build Coastguard Worker printf(
171*5a923131SAndroid Build Coastguard Worker "Usage: %s patch <path to src erofs image> <path to dst erofs imaeg> "
172*5a923131SAndroid Build Coastguard Worker "<path "
173*5a923131SAndroid Build Coastguard Worker "of file inside erofs image> <patch file>\n",
174*5a923131SAndroid Build Coastguard Worker argv[0]);
175*5a923131SAndroid Build Coastguard Worker return 3;
176*5a923131SAndroid Build Coastguard Worker }
177*5a923131SAndroid Build Coastguard Worker const char* inode_path = argv[4];
178*5a923131SAndroid Build Coastguard Worker const char* patch_file = argv[5];
179*5a923131SAndroid Build Coastguard Worker return ExecuteLz4diffOp(
180*5a923131SAndroid Build Coastguard Worker src_image_path, dst_image_path, inode_path, patch_file, PATCH);
181*5a923131SAndroid Build Coastguard Worker } else if (op == "test") {
182*5a923131SAndroid Build Coastguard Worker if (argc != 4) {
183*5a923131SAndroid Build Coastguard Worker printf(
184*5a923131SAndroid Build Coastguard Worker "Usage: %s test <path to src erofs image> <path to dst erofs imaeg> "
185*5a923131SAndroid Build Coastguard Worker "\n",
186*5a923131SAndroid Build Coastguard Worker argv[0]);
187*5a923131SAndroid Build Coastguard Worker return 4;
188*5a923131SAndroid Build Coastguard Worker }
189*5a923131SAndroid Build Coastguard Worker return ExecuteLz4diffOp(
190*5a923131SAndroid Build Coastguard Worker src_image_path, dst_image_path, nullptr, nullptr, TEST);
191*5a923131SAndroid Build Coastguard Worker } else {
192*5a923131SAndroid Build Coastguard Worker LOG(ERROR) << "Unrecognized op " << op;
193*5a923131SAndroid Build Coastguard Worker return 4;
194*5a923131SAndroid Build Coastguard Worker }
195*5a923131SAndroid Build Coastguard Worker
196*5a923131SAndroid Build Coastguard Worker return 0;
197*5a923131SAndroid Build Coastguard Worker }