/*

 * Copyright (c) 2023-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.

 */



#ifndef CODE_SIGN_FSVERITY_UTILS_HELPER_H

#define CODE_SIGN_FSVERITY_UTILS_HELPER_H



#include <cerrno>

#include <cstdint>

#include <cstdlib>

#include <cstring>

#include <fcntl.h>

#include <libfsverity.h>

#include <sys/stat.h>

#include <unistd.h>



#include "errcode.h"

#include "byte_buffer.h"

#include "log.h"

#include "fdsan.h"



namespace OHOS {

namespace Security {

namespace CodeSign {

class FsverityUtilsHelper {

public:

    static FsverityUtilsHelper &GetInstance();

    bool GenerateFormattedDigest(const char *path, ByteBuffer &ret);

    bool GenerateFormattedDigestFromFd(int32_t fd, ByteBuffer &ret);

    static void ErrorMsgLogCallback(const char *msg);



private:

    FsverityUtilsHelper();

    ~FsverityUtilsHelper();



    FsverityUtilsHelper(const FsverityUtilsHelper &source) = delete;

    FsverityUtilsHelper &operator = (const FsverityUtilsHelper &source) = delete;



    void Init();

    bool ComputeDigest(const char *path, struct libfsverity_digest **digest);

    bool ComputeDigestFromFd(int32_t fd, struct libfsverity_digest **digest);

    bool FormatDigest(libfsverity_digest *digest, uint8_t *buffer);



    class FileReader {

    public:

        bool Open(const char *path)

        {

            if (path == nullptr) {

                LOG_ERROR("Invalid parameter, path is null.");

                return false;

            }

            if (fd_ > 0) {

                LOG_ERROR("File is already opened.");

                return false;

            }

            fd_ = open(path, O_RDONLY);

            if (fd_ <= 0) {

                LOG_ERROR("Open file failed, path = %{public}s, errno = <%{public}d, %{public}s>",

                    path, errno, strerror(errno));

                return false;

            }

            FDSAN_MARK(fd_);

            return true;

        }



        bool OpenFromFd(int32_t fd)

        {

            if (fd_ > 0) {

                LOG_ERROR("File is already opened.");

                return false;

            }

            if (fd <= 0) {

                LOG_ERROR("Invalid fd=%{public}d", fd);

                return false;

            }

            fd_ = dup(fd);

            if (fd_ <= 0) {

                LOG_ERROR("dup fd failed, errno = <%{public}d, %{public}s>", errno, strerror(errno));

                return false;

            }

            FDSAN_MARK(fd_);

            return true;

        }



        bool GetFileSize(uint64_t *size)

        {

            if (size == nullptr) {

                LOG_ERROR("Invalid parameter, size is null.");

                return false;

            }

            struct stat st;

            if (fstat(fd_, &st) != 0) {

                LOG_ERROR("Stat file failed, errno = <%{public}d, %{public}s>",

                    errno, strerror(errno));

                return false;

            }

            *size = st.st_size;

            return true;

        }



        ~FileReader()

        {

            if (fd_ > 0) {

                FDSAN_CLOSE(fd_);

                fd_ = -1;

            }

        }



        static int ReadFileCallback(void *f, void *buf, size_t count)

        {

            if (f == nullptr || buf == nullptr) {

                return CS_ERR_FILE_READ;

            }

            FileReader *reader = static_cast<FileReader *>(f);

            return reader->ReadBytes(static_cast<uint8_t *>(buf), count);

        }



    private:

        int ReadBytes(uint8_t *buf, size_t count)

        {

            if (buf == nullptr) {

                return CS_ERR_FILE_READ;

            }

            if (fd_ <= 0) {

                return CS_ERR_FILE_READ;

            }

            while (count) {

                ssize_t bytesRead = read(fd_, buf, count);

                if (bytesRead <= 0) {

                    return CS_ERR_FILE_READ;

                }

                buf += bytesRead;

                count -= static_cast<size_t>(bytesRead);

            }

            return CS_SUCCESS;

        }



        int fd_ = -1;

    };

};

}

}

}

#endif