/*
* Copyright (c) Huawei Technologies Co., Ltd. 2025-2026. All rights reserved.
* ubs-virt-ovs is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*          http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
#include "../include/utils.h"
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include "../include/common.h"

void *map_share_mem(const char *shmID, size_t size)
{
    if (shmID == NULL || size == 0) {
        LOG_ERROR("shmID or size is NULL");
        return NULL;
    }
    int fd = shm_open(shmID, O_CREAT | O_RDWR, S_IWUSR | S_IRUSR);
    CHECK_COND_RETURN_(fd == -1, NULL, "Failed to shm_open, fd = %d.", fd);

    struct stat st;
    if (fstat(fd, &st) != 0) {
        LOG_ERROR("Failed to fstat");
        close(fd);
        return NULL;
    }
    if (st.st_size >= 0 && (size_t)st.st_size < size) {
        if (ftruncate(fd, size) == -1) {
            LOG_ERROR("Failed to ftruncate");
            close(fd);
            return NULL;
        }
    }

    void *addr_ = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    close(fd);

    if (addr_ == MAP_FAILED) {
        LOG_ERROR("Failed to get shared memory");
        return NULL;
    }

    return addr_;
}

static bool file_lock_acquire(file_lock *lock, int operation)
{
    if (!lock) {
        LOG_ERROR("file_lock_acquire: lock is NULL");
        return false;
    }

    if (lock->fd == -1) {
        LOG_ERROR("file_lock_acquire: invalid file descriptor");
        return false;
    }

    if (lock->held) {
        LOG_ERROR("file_lock_acquire: lock already held");
        return false;
    }

    int ret = flock(lock->fd, operation);
    if (ret != 0) {
        LOG_ERROR("lock failed, fd %d, errno %s", lock->fd, strerror(errno));
        return false;
    }

    lock->held = true;
    return true;
}

file_lock file_lock_create(const char *path, int operation)
{
    file_lock lock = {-1, false};

    if (!path) {
        LOG_ERROR("file_lock_create: path is NULL");
        return lock;
    }

    lock.fd = open(path, O_CREAT | O_RDONLY, 0);
    if (lock.fd == -1) {
        LOG_ERROR("file_lock_create: open file failed, path: %s", path);
        return lock;
    }

    if (operation != 0) {
        lock.held = file_lock_acquire(&lock, operation);
    }

    return lock;
}

static bool file_lock_release(file_lock *lock)
{
    if (!lock) {
        LOG_ERROR("file_lock_release: lock is NULL");
        return false;
    }

    if (!lock->held) {
        LOG_ERROR("file_lock_release: lock not held");
        return false;
    }

    int ret = flock(lock->fd, LOCK_UN);
    CHECK_COND_RETURN_(ret != 0, false, "unlock failed, fd %d, errno %s.", lock->fd, strerror(errno));

    lock->held = false;
    return true;
}

void file_lock_destroy(file_lock *lock)
{
    if (!lock) {
        return;
    }

    if (lock->fd == -1) {
        return;
    }

    if (lock->held) {
        file_lock_release(lock);
    }

    if (close(lock->fd) == -1) {
        LOG_ERROR("close file failed, fd %d, errno is %s", lock->fd, strerror(errno));
    }

    lock->fd = -1;
    lock->held = false;
}