root/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/Settings.cpp
// Settings.cpp

#include <new>

#include <stdio.h>
#include <stdlib.h>

#include <driver_settings.h>
#include <FindDirectory.h>

#include "Debug.h"
#include "HashMap.h"
#include "IOCtlInfo.h"
#include "Settings.h"

using std::nothrow;

static const directory_which kDirectories[] = {
        B_USER_NONPACKAGED_DATA_DIRECTORY,
        B_USER_DATA_DIRECTORY,
        B_SYSTEM_NONPACKAGED_DATA_DIRECTORY,
        B_SYSTEM_DATA_DIRECTORY
};
static const char *kFSSubpath = "/userlandfs/file_systems/";

// IOCtlInfoMap
struct Settings::IOCtlInfoMap : public HashMap<HashKey32<int>, IOCtlInfo*> {
};


// _FindNextParameter
template<typename container_t>
static
const driver_parameter *
_FindNextParameter(const container_t *container, const char *name,
        int32 &cookie)
{
        const driver_parameter *parameter = NULL;
        if (container) {
                for (; !parameter && cookie < container->parameter_count; cookie++) {
                        const driver_parameter &param = container->parameters[cookie];
                        if (!strcmp(param.name, name))
                                parameter = &param;
                }
        }
        return parameter;
}

// _GetParameterValue
template<typename container_t>
static
const char *
_GetParameterValue(const container_t *container, const char *name,
        const char *unknownValue, const char *noArgValue)
{
        if (container) {
                for (int32 i = container->parameter_count - 1; i >= 0; i--) {
                        const driver_parameter &param = container->parameters[i];
                        if (!strcmp(param.name, name)) {
                                if (param.value_count > 0)
                                        return param.values[0];
                                return noArgValue;
                        }
                }
        }
        return unknownValue;
}

// contains
static inline
bool
contains(const char **array, size_t size, const char *value)
{
        for (int32 i = 0; i < (int32)size; i++) {
                if (!strcmp(array[i], value))
                        return true;
        }
        return false;
}

// _GetParameterValue
template<typename container_t>
static
bool
_GetParameterValue(const container_t *container, const char *name,
        bool unknownValue, bool noArgValue)
{
        // note: container may be NULL
        const char unknown = 0;
        const char noArg = 0;
        const char *value = _GetParameterValue(container, name, &unknown, &noArg);
        if (value == &unknown)
                return unknownValue;
        if (value == &noArg)
                return noArgValue;
        const char *trueStrings[]
                = { "1", "true", "yes", "on", "enable", "enabled" };
        const char *falseStrings[]
                = { "0", "false", "no", "off", "disable", "disabled" };
        if (contains(trueStrings, sizeof(trueStrings) / sizeof(const char*),
                                 value)) {
                return true;
        }
        if (contains(falseStrings, sizeof(falseStrings) / sizeof(const char*),
                                 value)) {
                return false;
        }
        return unknownValue;
}

// _GetParameterValue
template<typename container_t>
static
int
_GetParameterValue(const container_t *container, const char *name,
        int unknownValue, int noArgValue)
{
        // note: container may be NULL
        const char unknown = 0;
        const char noArg = 0;
        const char *value = _GetParameterValue(container, name, &unknown, &noArg);
        if (value == &unknown)
                return unknownValue;
        if (value == &noArg)
                return noArgValue;
        return atoi(value);
}

// _FindFSParameter
static
const driver_parameter *
_FindFSParameter(const driver_settings *settings, const char *name)
{
        if (settings) {
                int32 cookie = 0;
                while (const driver_parameter *parameter
                                = _FindNextParameter(settings, "file_system", cookie)) {
PRINT(("  found file_system parameter\n"));
if (parameter->value_count > 0)
PRINT(("    value: `%s'\n", parameter->values[0]));
                        if (parameter->value_count == 1
                                && !strcmp(parameter->values[0], name)) {
                                return parameter;
                        }
                }
        }
        return NULL;
}

// constructor
Settings::Settings()
        : fIOCtlInfos(NULL)
{
}

// destructor
Settings::~Settings()
{
        Unset();
}

// SetTo
status_t
Settings::SetTo(const char* fsName)
{
        if (!fsName)
                RETURN_ERROR(B_BAD_VALUE);
        // unset
        Unset();
        // create the ioctl info map
        fIOCtlInfos = new(nothrow) IOCtlInfoMap;
        if (!fIOCtlInfos)
                RETURN_ERROR(B_NO_MEMORY);

        // load the driver settings for the FS
        char path[B_PATH_NAME_LENGTH];
        for (size_t i = 0; i < sizeof(kDirectories) / sizeof(kDirectories[0]);
                        i++) {
                if (find_directory(kDirectories[i], -1, false, (char*)&path,
                                B_PATH_NAME_LENGTH) != B_OK) {
                        continue;
                }

                // construct the path within the directory
                strlcat(path, kFSSubpath, B_PATH_NAME_LENGTH);
                strlcat(path, fsName, B_PATH_NAME_LENGTH);

                // load the file at the constructed path
                void *settings = load_driver_settings((char*)&path);
                if (!settings)
                        continue;

                // get the settings from the loaded file
                const driver_settings *ds = get_driver_settings(settings);
                if (!ds) {
                        unload_driver_settings(settings);
                        continue;
                }

                // get the parameter from the settings
                const driver_parameter *fsParameter = NULL;
                fsParameter = _FindFSParameter(ds, fsName);

                //  init the object and unload the settings
                if (fsParameter)
                        _Init(ds, fsParameter);
                unload_driver_settings(settings);
                
                // if we found the parameter, we're done
                if (fsParameter)
                        return B_OK;
        }

        // if we get here, we did not find the parameter
        return B_ENTRY_NOT_FOUND;
}

// Unset
void
Settings::Unset()
{
        if (fIOCtlInfos) {
                for (IOCtlInfoMap::Iterator it = fIOCtlInfos->GetIterator();
                         it.HasNext();) {
                        IOCtlInfoMap::Entry entry = it.Next();
                        delete entry.value;
                }
                delete fIOCtlInfos;
                fIOCtlInfos = NULL;
        }
}

// GetIOCtlInfo
const IOCtlInfo*
Settings::GetIOCtlInfo(int command) const
{
        return (fIOCtlInfos ? fIOCtlInfos->Get(command) : NULL);
}

// Dump
void
Settings::Dump() const
{
        D(
                PRINT(("Settings:\n"));
                if (fIOCtlInfos) {
                        for (IOCtlInfoMap::Iterator it = fIOCtlInfos->GetIterator();
                                 it.HasNext();) {
                                IOCtlInfoMap::Entry entry = it.Next();
                                IOCtlInfo* info = entry.value;
                                PRINT(("  ioctl %d: buffer size: %" B_PRId32
                                        ", write buffer size: %" B_PRId32 "\n", info->command,
                                        info->bufferSize, info->writeBufferSize));
                        }
                }
        )
}

// _Init
status_t
Settings::_Init(const driver_settings *settings,
                                const driver_parameter *fsParams)
{
PRINT(("Settings::_Init(%p, %p)\n", settings, fsParams));
        status_t error = B_OK;
        int32 cookie = 0;
        while (const driver_parameter *parameter
                        = _FindNextParameter(fsParams, "ioctl", cookie)) {
                if (parameter->value_count == 1) {
                        int command = atoi(parameter->values[0]);
                        if (command > 0) {
                                IOCtlInfo* info = fIOCtlInfos->Remove(command);
                                if (!info) {
                                        info = new(nothrow) IOCtlInfo;
                                        if (!info)
                                                RETURN_ERROR(B_NO_MEMORY);
                                }
                                info->command = command;
                                info->bufferSize
                                        = _GetParameterValue(parameter, "buffer_size", 0, 0);
                                info->writeBufferSize
                                        = _GetParameterValue(parameter, "write_buffer_size", 0, 0);
                                info->isBuffer = _GetParameterValue(parameter, "is_buffer",
                                        false, false);
                                error = fIOCtlInfos->Put(command, info);
                                if (error != B_OK) {
                                        delete info;
                                        return error;
                                }
                        }
                }
        }
PRINT(("Settings::_Init() done: %s\n", strerror(error)));
        return error;
}