1/*
2 * Copyright (c) 2021 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 "command_process.h"
17#include <cstdio>
18#include <fcntl.h>
19#include <linux/fs.h>
20#include <memory>
21#include <pthread.h>
22#include <sys/ioctl.h>
23#include <sys/stat.h>
24#include <sys/types.h>
25#include <unistd.h>
26#include "applypatch/block_set.h"
27#include "applypatch/block_writer.h"
28#include "applypatch/data_writer.h"
29#include "applypatch/store.h"
30#include "applypatch/transfer_manager.h"
31#include "log/log.h"
32#include "securec.h"
33#include "utils.h"
34
35using namespace Hpackage;
36using namespace Updater::Utils;
37namespace Updater {
38CommandResult AbortCommandFn::Execute(const Command &params)
39{
40    return SUCCESS;
41}
42
43CommandResult NewCommandFn::Execute(const Command &params)
44{
45    BlockSet bs;
46    bs.ParserAndInsert(params.GetArgumentByPos(1));
47    LOG(INFO) << " writing " << bs.TotalBlockSize() << " blocks of new data";
48    auto writerThreadInfo = params.GetTransferParams()->writerThreadInfo.get();
49    pthread_mutex_lock(&writerThreadInfo->mutex);
50    writerThreadInfo->writer = std::make_unique<BlockWriter>(params.GetFileDescriptor(), bs);
51    pthread_cond_broadcast(&writerThreadInfo->cond);
52    while (writerThreadInfo->writer != nullptr) {
53        LOG(DEBUG) << "wait for new data write done...";
54        if (!writerThreadInfo->readyToWrite) {
55            LOG(ERROR) << "writer thread could not write blocks. " << bs.TotalBlockSize() * H_BLOCK_SIZE -
56                writerThreadInfo->writer->GetTotalWritten() << " bytes lost";
57            pthread_mutex_unlock(&writerThreadInfo->mutex);
58            writerThreadInfo->writer.reset();
59            writerThreadInfo->writer = nullptr;
60            return FAILED;
61        }
62        LOG(DEBUG) << "Writer already written " << writerThreadInfo->writer->GetTotalWritten() << " byte(s)";
63        pthread_cond_wait(&writerThreadInfo->cond, &writerThreadInfo->mutex);
64    }
65    pthread_mutex_unlock(&writerThreadInfo->mutex);
66
67    writerThreadInfo->writer.reset();
68    params.GetTransferParams()->written += bs.TotalBlockSize();
69    return SUCCESS;
70}
71
72CommandResult ZeroAndEraseCommandFn::Execute(const Command &params)
73{
74    bool isErase = false;
75    if (params.GetCommandType() == CommandType::ERASE) {
76        isErase = true;
77        LOG(INFO) << "Start run ERASE command";
78    }
79    if (isErase) {
80        struct stat statBlock {};
81        if (fstat(params.GetFileDescriptor(), &statBlock) == -1) {
82            LOG(ERROR) << "Failed to fstat";
83            return FAILED;
84        }
85#ifndef UPDATER_UT
86        if (!S_ISBLK(statBlock.st_mode)) {
87            LOG(ERROR) << "Invalid block device";
88            return FAILED;
89        }
90#endif
91    }
92
93    BlockSet blk;
94    blk.ParserAndInsert(params.GetArgumentByPos(1));
95    LOG(INFO) << "Parser params to block set";
96    auto ret = CommandResult(blk.WriteZeroToBlock(params.GetFileDescriptor(), isErase));
97    if (ret == SUCCESS && !isErase) {
98        params.GetTransferParams()->written += blk.TotalBlockSize();
99    }
100    return ret;
101}
102
103bool LoadTarget(const Command &params, size_t &pos, std::vector<uint8_t> &buffer,
104    BlockSet &targetBlock, CommandResult &result)
105{
106    CommandType type = params.GetCommandType();
107    // Read sha256 of source and target
108    std::string srcHash = params.GetArgumentByPos(pos++);
109    std::string tgtHash = srcHash;
110    if (type != CommandType::MOVE) {
111        tgtHash = params.GetArgumentByPos(pos++);
112    }
113
114    // Read the target's buffer to determine whether it needs to be written
115    std::string cmdTmp = params.GetArgumentByPos(pos++);
116    targetBlock.ParserAndInsert(cmdTmp);
117    size_t tgtBlockSize = targetBlock.TotalBlockSize() * H_BLOCK_SIZE;
118    std::vector<uint8_t> tgtBuffer(tgtBlockSize);
119
120    if (targetBlock.ReadDataFromBlock(params.GetFileDescriptor(), tgtBuffer) == 0) {
121        LOG(ERROR) << "Read data from block error, TotalBlockSize: " << targetBlock.TotalBlockSize();
122        result = FAILED;
123        return false;
124    }
125    if (targetBlock.VerifySha256(tgtBuffer, targetBlock.TotalBlockSize(), tgtHash) == 0) {
126        LOG(ERROR) << "Will write same sha256 blocks to target, no need to write";
127        result = SUCCESS;
128        return false;
129    }
130    std::vector<uint8_t>().swap(tgtBuffer);
131    std::string blockLen = params.GetArgumentByPos(pos++);
132    size_t srcBlockSize = String2Int<size_t>(blockLen, N_DEC);
133    buffer.resize(srcBlockSize * H_BLOCK_SIZE);
134    if (targetBlock.LoadTargetBuffer(params, buffer, srcBlockSize, pos, srcHash) != 0) {
135        LOG(ERROR) << "Failed to load blocks";
136        result = FAILED;
137        return false;
138    }
139    result = SUCCESS;
140    return true;
141}
142
143int32_t DiffAndMoveCommandFn::WriteDiffToBlock(const Command &params, std::vector<uint8_t> &srcBuffer,
144                                               uint8_t *patchBuffer, size_t patchLength, BlockSet &targetBlock)
145{
146    CommandType type = params.GetCommandType();
147    return targetBlock.WriteDiffToBlock(params, srcBuffer, patchBuffer, patchLength, type == CommandType::IMGDIFF);
148}
149
150CommandResult DiffAndMoveCommandFn::Execute(const Command &params)
151{
152    CommandType type = params.GetCommandType();
153    size_t pos = H_DIFF_CMD_ARGS_START;
154    if (type == CommandType::MOVE) {
155        pos = H_MOVE_CMD_ARGS_START;
156    }
157
158    BlockSet targetBlock;
159    std::vector<uint8_t> buffer;
160    CommandResult result = FAILED;
161    if (!LoadTarget(params, pos, buffer, targetBlock, result)) {
162        return result;
163    }
164    if (!params.GetTransferParams()->canWrite) {
165        return result;
166    }
167
168    int32_t ret = -1;
169    if (type != CommandType::MOVE) {
170        pos = H_MOVE_CMD_ARGS_START;
171        size_t offset = Utils::String2Int<size_t>(params.GetArgumentByPos(pos++), Utils::N_DEC);
172        size_t patchLength = Utils::String2Int<size_t>(params.GetArgumentByPos(pos++), Utils::N_DEC);
173        uint8_t *patchBuffer = params.GetTransferParams()->patchDataBuffer + offset;
174        ret = WriteDiffToBlock(params, buffer, patchBuffer, patchLength, targetBlock);
175    } else {
176        ret = targetBlock.WriteDataToBlock(params.GetFileDescriptor(), buffer) == 0 ? -1 : 0;
177    }
178    if (ret != 0) {
179        LOG(ERROR) << "fail to write block data.";
180        return errno == EIO ? NEED_RETRY : FAILED;
181    }
182    std::string storeBase = params.GetTransferParams()->storeBase;
183    std::string freeStash = params.GetTransferParams()->freeStash;
184    if (!freeStash.empty()) {
185        if (Store::FreeStore(storeBase, freeStash) != 0) {
186            LOG(WARNING) << "fail to delete file: " << freeStash;
187        }
188        params.GetTransferParams()->freeStash.clear();
189    }
190    params.GetTransferParams()->written += targetBlock.TotalBlockSize();
191    return SUCCESS;
192}
193
194CommandResult FreeCommandFn::Execute(const Command &params)
195{
196    std::string shaStr = params.GetArgumentByPos(1);
197    std::string storeBase = params.GetTransferParams()->storeBase;
198    if (params.GetTransferParams()->storeCreated == 0) {
199        return CommandResult(Store::FreeStore(storeBase, shaStr));
200    }
201    return SUCCESS;
202}
203
204CommandResult StashCommandFn::Execute(const Command &params)
205{
206    size_t pos = 1;
207    const std::string shaStr = params.GetArgumentByPos(pos++);
208    BlockSet srcBlk;
209    LOG(INFO) << "Get source block info to block set";
210    srcBlk.ParserAndInsert(params.GetArgumentByPos(pos++));
211    size_t srcBlockSize = srcBlk.TotalBlockSize();
212    std::vector<uint8_t> buffer;
213    buffer.resize(srcBlockSize * H_BLOCK_SIZE);
214    std::string storeBase = params.GetTransferParams()->storeBase;
215    LOG(DEBUG) << "Confirm whether the block is stored";
216    if (Store::LoadDataFromStore(storeBase, shaStr, buffer) == 0) {
217        LOG(INFO) << "The stash has been stored, skipped";
218        return SUCCESS;
219    }
220    LOG(DEBUG) << "Read block data to buffer";
221    if (srcBlk.ReadDataFromBlock(params.GetFileDescriptor(), buffer) == 0) {
222        LOG(ERROR) << "Error to load block data";
223        return FAILED;
224    }
225    int32_t res = srcBlk.VerifySha256(buffer, srcBlockSize, shaStr);
226    if (res != 0 && !params.GetTransferParams()->canWrite) {
227        res = BlockVerify(params, buffer, srcBlockSize, shaStr, pos);
228    }
229    if (res != 0) {
230        LOG(WARNING) << "failed to load source blocks for stash";
231        return SUCCESS;
232    }
233    LOG(INFO) << "store " << srcBlockSize << " blocks to " << storeBase << "/" << shaStr;
234    int ret = Store::WriteDataToStore(storeBase, shaStr, buffer, srcBlockSize * H_BLOCK_SIZE);
235    return CommandResult(ret);
236}
237} // namespace Updater
238