/*

 * Copyright (c) 2021 Huawei Device Co., Ltd.

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *     http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */



#include "diffpatch.h"

#ifndef __WIN32

#include <climits>

#include <sys/mman.h>

#endif

#include <cstdlib>

#include <fcntl.h>

#include <sys/stat.h>

#include <unistd.h>

#include <vector>

#include "openssl/sha.h"

#include "pkg_utils.h"



namespace UpdatePatch {

using namespace Updater;



MemMapInfo::~MemMapInfo()

{

    if (memory != nullptr) {

        munmap(memory, length);

    }

    memory = nullptr;

    if (fd != -1) {

#ifndef DIFF_PATCH_SDK

        fdsan_close_with_tag(fd, FDSAN_UPDATER_TAG);

#else

        close(fd);

#endif

        fd = -1;

    }

}



int32_t WriteDataToFile(const std::string &fileName, const std::vector<uint8_t> &data, size_t dataSize)

{

    std::ofstream patchFile(fileName, std::ios::out | std::ios::binary);

    if (!patchFile) {

        PATCH_LOGE("Failed to open %s", fileName.c_str());

        return -1;

    }

    patchFile.write(reinterpret_cast<const char*>(data.data()), dataSize);

    patchFile.close();

    return PATCH_SUCCESS;

}



int32_t PatchMapFile(const std::string &fileName, MemMapInfo &info)

{

    char realPath[PATH_MAX] = { 0 };

#ifdef _WIN32

    if (_fullpath(realPath, fileName.c_str(), PATH_MAX) == nullptr) {

#else

    if (realpath(fileName.c_str(), realPath) == nullptr) {

#endif

        PATCH_LOGE("Failed to get realpath %s", fileName.c_str());

        return -1;

    }

    info.fd = open(realPath, O_RDONLY);

    if (info.fd < 0) {

        PATCH_LOGE("Failed to open file %s", fileName.c_str());

        return -1;

    }

#ifndef DIFF_PATCH_SDK

    fdsan_exchange_owner_tag(info.fd, 0, FDSAN_UPDATER_TAG);

#endif

    struct stat st {};

    int32_t ret = fstat(info.fd, &st);

    if (ret < 0) {

        PATCH_LOGE("Failed to fstat");

        return ret;

    }

    if (S_ISBLK(st.st_mode)) {

        st.st_size = lseek(info.fd, 0, SEEK_END);

        lseek(info.fd, 0, SEEK_SET);

    }



    void *mappedData = mmap(nullptr, st.st_size, PROT_READ, MAP_PRIVATE, info.fd, 0);

    if (mappedData == MAP_FAILED) {

#ifndef DIFF_PATCH_SDK

        fdsan_close_with_tag(info.fd, FDSAN_UPDATER_TAG);

#else

        close(info.fd);

#endif

        info.fd = -1;

        PATCH_LOGE("Failed to memory map");

        return -1;

    }

    info.memory = static_cast<uint8_t*>(mappedData);

    info.length = static_cast<size_t>(st.st_size);

    return PATCH_SUCCESS;

}



std::string GeneraterBufferHash(const BlockBuffer &buffer)

{

    SHA256_CTX sha256Ctx;

    SHA256_Init(&sha256Ctx);

    SHA256_Update(&sha256Ctx, buffer.buffer, buffer.length);

    std::vector<uint8_t> digest(SHA256_DIGEST_LENGTH);

    SHA256_Final(digest.data(), &sha256Ctx);

    return ConvertSha256Hex({

            digest.data(), SHA256_DIGEST_LENGTH

        });

}



std::string ConvertSha256Hex(const BlockBuffer &buffer)

{

    const std::string hexChars = "0123456789abcdef";

    std::string haxSha256 = "";

    unsigned int c;

    for (size_t i = 0; i < buffer.length; ++i) {

        auto d = buffer.buffer[i];

        c = (d >> SHIFT_RIGHT_FOUR_BITS) & 0xf;     // last 4 bits

        haxSha256.push_back(hexChars[c]);

        haxSha256.push_back(hexChars[d & 0xf]);

    }

    return haxSha256;

}

} // namespace UpdatePatch