/*
 * Copyright (c) 2024 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 "key_utils.h"

#include <cstdlib>
#include <dlfcn.h>
#include <fcntl.h>
#include <string>
#include <unistd.h>
#include <securec.h>

#include "log_rust.h"
#include "fdsan.h"

using namespace OHOS::Security::CodeSign;

enum DeviceMode {
    NOT_INITIALIZE = 0,
    DEVICE_MODE_RD,
    DEVICE_MODE_NOT_RD
};

constexpr int32_t CMDLINE_MAX_BUF_LEN = 4096;
#ifndef KEY_ENABLE_UTILS_TEST
static const std::string PROC_CMDLINE_FILE_PATH = "/proc/cmdline";
static int32_t g_isRdDevice = NOT_INITIALIZE;
#else
const std::string PROC_CMDLINE_FILE_PATH = "/data/test/tmp/cmdline";
int32_t g_isRdDevice = NOT_INITIALIZE;
#endif

static bool CheckDeviceMode(char *buf, ssize_t bufLen)
{
    bool status = false;
    char *onStr = strstr(buf, "oemmode=rd");
    char *offStr = strstr(buf, "oemmode=user");
    char *statusStr = strstr(buf, "oemmode=");
    if (onStr == nullptr && offStr == nullptr) {
        LOG_INFO(LABEL, "Not rd mode, cmdline = %{private}s", buf);
    } else if (offStr != nullptr && statusStr != nullptr && offStr != statusStr) {
        LOG_ERROR(LABEL, "cmdline attacked, cmdline = %{private}s", buf);
    } else if (onStr != nullptr && offStr == nullptr) {
        status = true;
        LOG_DEBUG(LABEL, "Oemode is rd");
    }
    return status;
}

static bool CheckEfuseStatus(char *buf, ssize_t bufLen)
{
    bool status = false;
    char *onStr = strstr(buf, "efuse_status=1");
    char *offStr = strstr(buf, "efuse_status=0");
    char *statusStr = strstr(buf, "efuse_status=");
    if (onStr == nullptr && offStr == nullptr) {
        LOG_INFO(LABEL, "device is efused, cmdline = %{private}s", buf);
    } else if (offStr != nullptr && statusStr != nullptr && offStr != statusStr) {
        LOG_ERROR(LABEL, "cmdline attacked, cmdline = %{private}s", buf);
    } else if (onStr != nullptr && offStr == nullptr) {
        status = true;
        LOG_DEBUG(LABEL, "device is not efused");
    }
    return status;
}

static void ParseCMDLine()
{
    int32_t fd = open(PROC_CMDLINE_FILE_PATH.c_str(), O_RDONLY);
    if (fd < 0) {
        g_isRdDevice = DEVICE_MODE_NOT_RD;
        LOG_ERROR(LABEL, "open %{public}s failed, %{public}s",
            PROC_CMDLINE_FILE_PATH.c_str(), strerror(errno));
        return;
    }
    FDSAN_MARK(fd);
    char *buf = nullptr;
    int32_t status = DEVICE_MODE_NOT_RD;
    do {
        buf = static_cast<char *>(malloc(CMDLINE_MAX_BUF_LEN));
        if (buf == nullptr) {
            LOG_ERROR(LABEL, "Alloc buffer for reading cmdline failed.");
            break;
        }
        (void) memset_s(buf, CMDLINE_MAX_BUF_LEN, 0, CMDLINE_MAX_BUF_LEN);
        ssize_t bufLen = read(fd, buf, CMDLINE_MAX_BUF_LEN - 1);
        if (bufLen < 0) {
            LOG_ERROR(LABEL, "Read %{public}s failed, %{public}s.",
                PROC_CMDLINE_FILE_PATH.c_str(), strerror(errno));
            break;
        }
        if (CheckDeviceMode(buf, bufLen) || CheckEfuseStatus(buf, bufLen)) {
            status = DEVICE_MODE_RD;
        }
    } while (0);
    g_isRdDevice = status;
    (void) FDSAN_CLOSE(fd);
    free(buf);
}

bool IsRdDevice()
{
    if (g_isRdDevice == NOT_INITIALIZE) {
        ParseCMDLine();
    }
    return g_isRdDevice == DEVICE_MODE_RD;
}