/* common/KMExports.h
 *
 * Copyright (C) 2024 OpenKylin, Inc
 */

#ifndef __KMEXPORTS_H__
#define __KMEXPORTS_H__

#include <map>
#include <vector>

typedef enum
{
    KAIMING_FILESYSTEM_MODE_NONE = 0,
    KAIMING_FILESYSTEM_MODE_READ_ONLY = 1,
    KAIMING_FILESYSTEM_MODE_READ_WRITE = 2,
    KAIMING_FILESYSTEM_MODE_CREATE = 3,
    KAIMING_FILESYSTEM_MODE_LAST = KAIMING_FILESYSTEM_MODE_CREATE
} KMFilesystemMode;

typedef struct
{
    std::string path;
    unsigned int mode;
} ExportedPath;

/*
 * KMExportsTestFlags:
 * @KAIMING_EXPORTS_TEST_FLAGS_AUTOFS: Pretend everything is an autofs.
 *
 * Flags used to provide mock behaviour during unit testing.
 */
typedef enum
{
    KAIMING_EXPORTS_TEST_FLAGS_AUTOFS = (1 << 0),
    KAIMING_EXPORTS_TEST_FLAGS_NONE = 0
} KMExportsTestFlags;

#define FAKE_MODE_DIR -1 /* Ensure a dir, either on tmpfs or mapped parent */
#define FAKE_MODE_TMPFS KAIMING_FILESYSTEM_MODE_NONE
#define FAKE_MODE_SYMLINK G_MAXINT

/*
 * @name: A file or directory below /etc
 * @test: How we test whether it is suitable
 *
 * The paths in /etc that are required if we want to make use of the
 * host /usr (and /lib, and so on).
 */
typedef struct
{
    std::string name;
    int ifmt;
} LibsNeedEtc;

/**
 * export command relations
 */
class KMExports
{
public:
    KMExports();
    ~KMExports();

    bool addPathExpose(KMFilesystemMode mode, const std::string &path, std::string &errMsg);
    bool addPathExposeOrHide(KMFilesystemMode mode, const std::string &path, std::string &errMsg);
    bool addPathTmpfs(const std::string &path, std::string &errMsg);
    bool addPathDir(const std::string &path, std::string &errMsg);
    void addHostOsExpose(KMFilesystemMode mode);
    void addHostEtcExpose(KMFilesystemMode mode);

    int openInHostAsyncSignalSafe(const std::string &absPath, int flags);

    std::string resolveLinkInHost(const std::string &absPath) const;

    void doExportPath(const std::string &path, unsigned int mode);

    bool pathIsVisible(const std::string &path) const;
    bool pathIsMapped(const std::vector<std::string> &keys, const std::string &path, bool &isReadonlyOut) const;
    bool pathParentIsMapped(const std::vector<std::string> &keys, const std::string &path) const;
    bool statInHost(const std::string &absPath, struct stat *buf, int flags) const;
    bool pathIsDir(const std::string &absPath) const;
    KMFilesystemMode pathGetMode(const std::string &path) const;
    std::string readlinkInHost(const std::string &absPath) const;

    const std::map<std::string, ExportedPath> &getExportedPaths() const
    {
        return m_hash;
    }

    KMFilesystemMode hostOs() const
    {
        return m_hostOs;
    }
    KMFilesystemMode hostEtc() const
    {
        return m_hostEtc;
    }
    int hostFd() const
    {
        return m_hostFd;
    }

public:
    static bool isExportMode(int mode);
    static bool neverExportAsSymlink(const std::string &path);

    static std::vector<LibsNeedEtc> m_libsNeedEtc;

private:
    bool exportsPathExpose(KMFilesystemMode mode, const std::string &path, int level, std::string &errMsg);

    std::map<std::string, ExportedPath> m_hash;
    KMFilesystemMode m_hostEtc = KAIMING_FILESYSTEM_MODE_NONE;
    KMFilesystemMode m_hostOs = KAIMING_FILESYSTEM_MODE_NONE;
    int m_hostFd = -1;
    // KMExportsTestFlags m_testFlags; TODO : 暂不提供mock单元测试功能
    static std::vector<std::string> m_dontExportIn;
    static std::vector<std::string> m_absUsrmergedDirs;
};

#endif