1 /*
2  * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
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  */
15 
16 #include "patch_shared.h"
17 
18 #include <string>
19 #include <unistd.h>
20 #include <cstdio>
21 #include <fcntl.h>
22 #include "log/log.h"
23 #include "applypatch/store.h"
24 #include "applypatch/transfer_manager.h"
25 #include "script_manager.h"
26 #include "applypatch/partition_record.h"
27 #include "utils.h"
28 #include "pkg_manager.h"
29 #include "updater_env.h"
30 
31 using namespace Uscript;
32 using namespace Hpackage;
33 using namespace Updater;
34 
35 namespace Updater {
36 constexpr int WRITE_FILE_SIZE = 4096;
37 struct UpdateBlockInfo {
38     std::string partitionName;
39     std::string transferName;
40     std::string newDataName;
41     std::string patchDataName;
42     std::string devPath;
43 };
44 
UpdatePathCheck(const std::string & updatePath,size_t length)45 static bool UpdatePathCheck(const std::string &updatePath, size_t length)
46 {
47     if (updatePath.empty() || length == 0) {
48         LOG(ERROR) << "updatePath is nullptr.";
49         return false;
50     }
51 
52     char realPath[PATH_MAX + 1] = {0};
53     if (realpath(updatePath.c_str(), realPath) == nullptr) {
54         LOG(ERROR) << "realPath is NULL" << " : " << strerror(errno);
55         return false;
56     }
57 
58     if (access(realPath, F_OK) != 0) {
59         LOG(ERROR) << "package does not exist!";
60         return false;
61     }
62 
63     return true;
64 }
65 
GetUpdateBlockInfo(UpdateBlockInfo & infos,const std::string & packagePath,const std::string & srcImage,const std::string & targetPath)66 static int GetUpdateBlockInfo(UpdateBlockInfo &infos, const std::string &packagePath,
67     const std::string &srcImage, const std::string &targetPath)
68 {
69     if (!UpdatePathCheck(packagePath, packagePath.length())) {
70         LOG(ERROR) << packagePath << " is empty.";
71         return -1;
72     }
73 
74     if (!UpdatePathCheck(srcImage, srcImage.length())) {
75         LOG(ERROR) << srcImage << " is empty.";
76         return -1;
77     }
78 
79     if (!UpdatePathCheck(targetPath, targetPath.length())) {
80         LOG(INFO) << "need to make store";
81         if (Updater::Utils::MkdirRecursive(targetPath, S_IRWXU) != 0) {
82             LOG(ERROR) << "Failed to make store";
83             return -1;
84         }
85     }
86 
87     infos.newDataName = "anco_hmos.new.dat";
88     infos.patchDataName = "anco_hmos.patch.dat";
89 
90     infos.transferName = "anco_hmos.transfer.list";
91     infos.devPath = srcImage;
92     infos.partitionName = "/anco_hmos";
93 
94     return 0;
95 }
96 
ExtractFileByNameFunc(Uscript::UScriptEnv & env,const std::string & fileName,Hpackage::PkgManager::StreamPtr & outStream,uint8_t * & outBuf,size_t & buffSize)97 static int32_t ExtractFileByNameFunc(Uscript::UScriptEnv &env, const std::string &fileName,
98                                      Hpackage::PkgManager::StreamPtr &outStream, uint8_t *&outBuf, size_t &buffSize)
99 {
100     if (env.GetPkgManager() == nullptr) {
101         LOG(ERROR) << "Error to get pkg manager";
102         return USCRIPT_ERROR_EXECUTE;
103     }
104 
105     const FileInfo *info = env.GetPkgManager()->GetFileInfo(fileName);
106     if (info == nullptr) {
107         LOG(ERROR) << "GetFileInfo fail";
108         return USCRIPT_ERROR_EXECUTE;
109     }
110     auto ret = env.GetPkgManager()->CreatePkgStream(outStream,
111         fileName, info->unpackedSize, PkgStream::PkgStreamType_MemoryMap);
112     if (ret != USCRIPT_SUCCESS || outStream == nullptr) {
113         LOG(ERROR) << "Error to create output stream";
114         return USCRIPT_ERROR_EXECUTE;
115     }
116     ret = env.GetPkgManager()->ExtractFile(fileName, outStream);
117     if (ret != USCRIPT_SUCCESS) {
118         LOG(ERROR) << "Error to extract file";
119         env.GetPkgManager()->ClosePkgStream(outStream);
120         return USCRIPT_ERROR_EXECUTE;
121     }
122     ret = outStream->GetBuffer(outBuf, buffSize);
123     LOG(INFO) << "outBuf data size is: " << buffSize;
124 
125     return USCRIPT_SUCCESS;
126 }
127 
ExecuteTransferCommand(int fd,const std::vector<std::string> & lines,TransferManagerPtr tm,const std::string & partitionName,const std::string & targetPath)128 static int32_t ExecuteTransferCommand(int fd, const std::vector<std::string> &lines,
129     TransferManagerPtr tm, const std::string &partitionName, const std::string &targetPath)
130 {
131     auto transferParams = tm->GetTransferParams();
132     auto writerThreadInfo = transferParams->writerThreadInfo.get();
133 
134     transferParams->storeBase = targetPath + partitionName + "_tmp";
135     LOG(INFO) << "Store base path is " << transferParams->storeBase;
136     int32_t ret = Store::CreateNewSpace(transferParams->storeBase, true);
137     if (ret == -1) {
138         LOG(ERROR) << "Error to create new store space";
139         return -1;
140     }
141     transferParams->storeCreated = ret;
142 
143     if (!tm->CommandsParser(fd, lines)) {
144         return Uscript::USCRIPT_ERROR_EXECUTE;
145     }
146     pthread_mutex_lock(&writerThreadInfo->mutex);
147     if (writerThreadInfo->readyToWrite) {
148         LOG(WARNING) << "New data writer thread is still available...";
149     }
150 
151     writerThreadInfo->readyToWrite = false;
152     pthread_cond_broadcast(&writerThreadInfo->cond);
153     pthread_mutex_unlock(&writerThreadInfo->mutex);
154     ret = pthread_join(transferParams->thread, nullptr);
155     std::ostringstream logMessage;
156     logMessage << "pthread join returned with " << ret;
157     if (ret != 0) {
158         LOG(WARNING) << logMessage.str();
159     }
160     if (transferParams->storeCreated != -1) {
161         Store::DoFreeSpace(transferParams->storeBase);
162         (void)Utils::DeleteFile(transferParams->storeBase);
163     }
164     return Uscript::USCRIPT_SUCCESS;
165 }
166 
DoExecuteUpdateBlock(const UpdateBlockInfo & infos,TransferManagerPtr tm,const std::vector<std::string> & lines,const std::string & targetPath,const std::string & dstImage)167 static int32_t DoExecuteUpdateBlock(const UpdateBlockInfo &infos, TransferManagerPtr tm,
168     const std::vector<std::string> &lines, const std::string &targetPath, const std::string &dstImage)
169 {
170     int fd = open(dstImage.c_str(), O_RDWR | O_LARGEFILE);
171     if (fd == -1) {
172         LOG(ERROR) << "Failed to open block";
173         return Uscript::USCRIPT_ERROR_EXECUTE;
174     }
175     int32_t ret = ExecuteTransferCommand(fd, lines, tm, infos.partitionName, targetPath);
176 
177     fsync(fd);
178     close(fd);
179     fd = -1;
180     if (ret == Uscript::USCRIPT_SUCCESS) {
181         PartitionRecord::GetInstance().RecordPartitionUpdateStatus(infos.partitionName, true);
182     }
183     (void)Utils::DeleteFile(infos.devPath);
184 
185     return ret;
186 }
187 
ExtractNewDataFunc(const PkgBuffer & buffer,size_t size,size_t start,bool isFinish,const void * context)188 static int ExtractNewDataFunc(const PkgBuffer &buffer, size_t size, size_t start, bool isFinish, const void* context)
189 {
190     void *p = const_cast<void *>(context);
191     WriterThreadInfo *info = static_cast<WriterThreadInfo *>(p);
192     uint8_t *addr = buffer.buffer;
193     while (size > 0) {
194         pthread_mutex_lock(&info->mutex);
195         while (info->writer == nullptr) {
196             if (!info->readyToWrite) {
197                 LOG(WARNING) << "writer is not ready to write.";
198                 pthread_mutex_unlock(&info->mutex);
199                 return Hpackage::PKG_INVALID_STREAM;
200             }
201             pthread_cond_wait(&info->cond, &info->mutex);
202         }
203         pthread_mutex_unlock(&info->mutex);
204         size_t toWrite = std::min(size, info->writer->GetBlocksSize() - info->writer->GetTotalWritten());
205         // No more data to write.
206         if (toWrite == 0) {
207             break;
208         }
209         bool ret = info->writer->Write(addr, toWrite, nullptr);
210         std::ostringstream logMessage;
211         logMessage << "Write " << toWrite << " byte(s) failed";
212         if (!ret) {
213             LOG(ERROR) << logMessage.str();
214             return Hpackage::PKG_INVALID_STREAM;
215         }
216         size -= toWrite;
217         addr += toWrite;
218 
219         if (info->writer->IsWriteDone()) {
220             pthread_mutex_lock(&info->mutex);
221             info->writer.reset();
222             pthread_cond_broadcast(&info->cond);
223             pthread_mutex_unlock(&info->mutex);
224         }
225     }
226     return Hpackage::PKG_SUCCESS;
227 }
228 
CondBroadcast(WriterThreadInfo * info)229 static inline void CondBroadcast(WriterThreadInfo *info)
230 {
231     pthread_mutex_lock(&info->mutex);
232     info->readyToWrite = false;
233     if (info->writer != nullptr) {
234         pthread_cond_broadcast(&info->cond);
235     }
236     pthread_mutex_unlock(&info->mutex);
237 }
238 
UnpackNewDataFunc(void * arg)239 void* UnpackNewDataFunc(void *arg)
240 {
241     TransferManagerPtr tm = static_cast<TransferManagerPtr>(arg);
242     WriterThreadInfo *info = tm->GetTransferParams()->writerThreadInfo.get();
243     Hpackage::PkgManager::StreamPtr stream = nullptr;
244     if (info->newPatch.empty()) {
245         LOG(ERROR) << "new patch file name is empty. thread quit.";
246         CondBroadcast(info);
247         return nullptr;
248     }
249     LOG(DEBUG) << "new patch file name: " << info->newPatch;
250     auto env = tm->GetTransferParams()->env;
251     const FileInfo *file = env->GetPkgManager()->GetFileInfo(info->newPatch);
252     if (file == nullptr) {
253         LOG(ERROR) << "Cannot get file info of :" << info->newPatch;
254         CondBroadcast(info);
255         return nullptr;
256     }
257     LOG(DEBUG) << info->newPatch << " info: size " << file->packedSize << " unpacked size " <<
258         file->unpackedSize << " name " << file->identity;
259     int32_t ret = env->GetPkgManager()->CreatePkgStream(stream, info->newPatch, ExtractNewDataFunc, info);
260     if (ret != Hpackage::PKG_SUCCESS || stream == nullptr) {
261         LOG(ERROR) << "Cannot extract " << info->newPatch << " from package.";
262         CondBroadcast(info);
263         return nullptr;
264     }
265     ret = env->GetPkgManager()->ExtractFile(info->newPatch, stream);
266     env->GetPkgManager()->ClosePkgStream(stream);
267     LOG(DEBUG) << "new data writer ending...";
268     // extract new data done.
269     // tell command.
270     CondBroadcast(info);
271     return nullptr;
272 }
273 
InitThread(const struct UpdateBlockInfo & infos,TransferManagerPtr tm)274 static int InitThread(const struct UpdateBlockInfo &infos, TransferManagerPtr tm)
275 {
276     auto transferParams = tm->GetTransferParams();
277     auto writerThreadInfo = transferParams->writerThreadInfo.get();
278     writerThreadInfo->readyToWrite = true;
279     pthread_mutex_init(&writerThreadInfo->mutex, nullptr);
280     pthread_cond_init(&writerThreadInfo->cond, nullptr);
281     pthread_attr_t attr;
282     pthread_attr_init(&attr);
283     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
284     writerThreadInfo->newPatch = infos.newDataName;
285     int error = pthread_create(&transferParams->thread, &attr, UnpackNewDataFunc, tm);
286     return error;
287 }
288 
CreateFixedSizeEmptyFile(const UpdateBlockInfo & infos,const std::string & filename,int64_t size)289 static int CreateFixedSizeEmptyFile(const UpdateBlockInfo &infos, const std::string &filename, int64_t size)
290 {
291     if (size <= 0) {
292         LOG(ERROR) << "size is " << size;
293         return -1;
294     }
295     if (!Updater::Utils::CopyFile(infos.devPath, filename)) {
296         LOG(ERROR) << "copy " << infos.devPath << " to " << filename << " failed";
297         return -1;
298     }
299     size_t fileSize = Updater::Utils::GetFileSize(infos.devPath);
300     if (static_cast<int64_t>(fileSize) >= size) {
301         LOG(INFO) << "no need copy";
302         return 0;
303     }
304     std::ofstream file(filename, std::ios::binary | std::ios::ate);
305     if (!file.is_open()) {
306         LOG(ERROR) << "Failed to open file for writing.";
307         return -1;
308     }
309 
310     /* fill the remaining space with zero values */
311     int writeFileTmp = (size - static_cast<int64_t>(fileSize)) / WRITE_FILE_SIZE;
312     char zerolist[WRITE_FILE_SIZE] = {0};
313     while (writeFileTmp > 0) {
314         file.write(zerolist, WRITE_FILE_SIZE);
315         writeFileTmp--;
316     }
317     writeFileTmp = (size - static_cast<int64_t>(fileSize)) % WRITE_FILE_SIZE;
318     char zero = 0;
319     while (writeFileTmp > 0) {
320         file.write(&zero, 1);
321         writeFileTmp--;
322     }
323 
324     file.close();
325     return 0;
326 }
327 
GetFileName(const std::string & srcImage)328 static std::string GetFileName(const std::string &srcImage)
329 {
330     std::vector<std::string> lines =
331         Updater::Utils::SplitString(std::string(srcImage), "/");
332     LOG(INFO) << "lines.size is " << lines.size();
333     if (lines.size() == 0) {
334         return "";
335     }
336     return lines[lines.size() - 1];
337 }
338 
ExecuteUpdateBlock(Uscript::UScriptEnv & env,const UpdateBlockInfo & infos,const std::string targetPath,std::string destImage)339 static int32_t ExecuteUpdateBlock(Uscript::UScriptEnv &env, const UpdateBlockInfo &infos,
340     const std::string targetPath, std::string destImage)
341 {
342     Hpackage::PkgManager::StreamPtr outStream = nullptr;
343     uint8_t *transferListBuffer = nullptr;
344     size_t transferListSize = 0;
345     if (ExtractFileByNameFunc(env, infos.transferName,
346         outStream, transferListBuffer, transferListSize) != USCRIPT_SUCCESS) {
347         return USCRIPT_ERROR_EXECUTE;
348     }
349 
350     std::unique_ptr<TransferManager> tm = std::make_unique<TransferManager>();
351     auto transferParams = tm->GetTransferParams();
352     /* Save Script Env to transfer manager */
353     transferParams->env = &env;
354     std::vector<std::string> lines =
355         Updater::Utils::SplitString(std::string(reinterpret_cast<const char*>(transferListBuffer)), "\n");
356     env.GetPkgManager()->ClosePkgStream(outStream);
357 
358     if (ExtractFileByNameFunc(env, infos.patchDataName, outStream,
359         transferParams->patchDataBuffer, transferParams->patchDataSize) != USCRIPT_SUCCESS) {
360         return USCRIPT_ERROR_EXECUTE;
361     }
362     env.GetPkgManager()->ClosePkgStream(outStream);
363     uint8_t *ancoSizeBuffer = nullptr;
364     size_t ancoSizeListSize = 0;
365     const std::string fileName = "anco_size";
366     if (ExtractFileByNameFunc(env, fileName, outStream, ancoSizeBuffer,
367                               ancoSizeListSize) != USCRIPT_SUCCESS) {
368         return USCRIPT_ERROR_EXECUTE;
369     }
370     env.GetPkgManager()->ClosePkgStream(outStream);
371     std::string str(reinterpret_cast<char*>(ancoSizeBuffer), ancoSizeListSize);
372     int64_t maxStashSize = std::stoll(str);
373     if (CreateFixedSizeEmptyFile(infos, destImage, maxStashSize) != 0) {
374         LOG(ERROR) << "Failed to create empty file";
375         return USCRIPT_ERROR_EXECUTE;
376     }
377 
378     LOG(INFO) << "Ready to start a thread to handle new data processing";
379     if (InitThread(infos, tm.get()) != 0) {
380         LOG(ERROR) << "Failed to create pthread";
381         return USCRIPT_ERROR_EXECUTE;
382     }
383     int32_t ret = DoExecuteUpdateBlock(infos, tm.get(), lines, targetPath, destImage);
384     return ret;
385 }
386 
RestoreOriginalFile(const std::string & packagePath,const std::string & srcImage,const std::string & targetPath)387 int RestoreOriginalFile(const std::string &packagePath, const std::string &srcImage, const std::string &targetPath)
388 {
389     UpdateBlockInfo infos {};
390     if (GetUpdateBlockInfo(infos, packagePath, srcImage, targetPath) != 0) {
391         return -1;
392     }
393     std::string destName = GetFileName(srcImage);
394     std::string destImage = targetPath + "/" + destName;
395 
396     PkgManager::PkgManagerPtr pkgManager = PkgManager::CreatePackageInstance();
397     if (pkgManager == nullptr) {
398         LOG(ERROR) << "pkgManager is nullptr";
399         return -1;
400     }
401 
402     std::vector<std::string> components;
403     std::string pckPath = packagePath;
404     int32_t ret = pkgManager->LoadPackage(pckPath, components, PkgFile::PKG_TYPE_ZIP);
405     if (ret != PKG_SUCCESS) {
406         LOG(ERROR) << "Fail to load package";
407         return -1;
408     }
409     PostMessageFunction postMessage = nullptr;
410     UScriptEnv *env = new (std::nothrow) UpdaterEnv(pkgManager, postMessage, false);
411     if (env == nullptr) {
412         LOG(ERROR) << "Fail to creat env";
413         return -1;
414     }
415 
416     int result = ExecuteUpdateBlock(*env, infos, targetPath, destImage);
417     if (result != 0) {
418         LOG(ERROR) << "restore original file fail.";
419     }
420     delete env;
421     env = nullptr;
422     return result;
423 }
424 }