<commit_before>#include <libgen.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>

#include <algorithm>
#include <iostream>

#include "configurator.hpp"

#include "common/foreach.hpp"
#include "common/params.hpp"
#include "common/string_utils.hpp"

using namespace mesos::internal;

const char* Configurator::DEFAULT_CONFIG_DIR = "conf";
const char* Configurator::CONFIG_FILE_NAME = "mesos.conf";
const char* Configurator::ENV_VAR_PREFIX = "MESOS_";

// Define a function for accessing the list of environment variables
// in a platform-independent way.
// On Mac OS X, the environ symbol isn't visible to shared libraries,
// so we must use the _NSGetEnviron() function (see man environ on OS X).
// On other platforms, it's fine to access environ from shared libraries.
#ifdef __APPLE__
#include "crt_externs.h"
namespace {
char** getEnviron() { return *_NSGetEnviron(); }
}
#else
extern char** environ;
namespace {
char** getEnviron() { return environ; }
}
#endif /* __APPLE__ */

Configurator::Configurator()
{
  addOption<string>("conf",
                    "Specifies a config directory from which to\n"
                    "read Mesos config files. The Mesos binaries\n"
                    "use <install location>src/conf by default");
}

void Configurator::validate()
{
  foreachpair (const string& key, const Option& opt, options) {
    if (params.contains(key) && opt.validator &&
        !opt.validator->isValid(params[key])) {
      string msg = "Invalid value for '" + key + "' option: " + params[key];
      throw ConfigurationException(msg.c_str());
    }
  }
}

Params& Configurator::load(int argc, char** argv, bool inferMesosHomeFromArg0)
{
  loadEnv();
  loadCommandLine(argc, argv, inferMesosHomeFromArg0);
  loadConfigFileIfGiven();
  loadDefaults();
  validate();
  return params;
}

Params& Configurator::load()
{
  loadEnv();
  loadConfigFileIfGiven();
  loadDefaults();
  validate();
  return params;
}

Params& Configurator::load(const map<string, string>& _params)
{
  loadEnv();
  params.loadMap(_params);
  loadConfigFileIfGiven();
  loadDefaults();
  validate();
  return params;
}

void Configurator::loadConfigFileIfGiven(bool overwrite) {
  if (params.contains("conf")) {
    // If conf param is given, always look for a config file in that directory
    string confDir = params["conf"];
    loadConfigFile(confDir + "/" + CONFIG_FILE_NAME, overwrite);
  } else if (params.contains("home")) {
    // Grab config file in MESOS_HOME/conf, if it exists
    string confDir = params["home"] + "/" + DEFAULT_CONFIG_DIR;
    string confFile = confDir + "/" + CONFIG_FILE_NAME;
    struct stat st;
    if (stat(confFile.c_str(), &st) == 0) {
      loadConfigFile(confFile, overwrite);
    }
  }
}

void Configurator::loadEnv(bool overwrite)
{
  char** environ = getEnviron();
  int i = 0;
  while (environ[i] != NULL) {
    string line = environ[i];
    if (line.find(ENV_VAR_PREFIX) == 0) {
      string key, val;
      size_t eq = line.find_first_of("=");
      if (eq == string::npos)
        continue; // ignore malformed lines (they shouldn't occur in environ!)
      key = line.substr(strlen(ENV_VAR_PREFIX), eq - strlen(ENV_VAR_PREFIX));
      std::transform(key.begin(), key.end(), key.begin(), ::tolower);
      val = line.substr(eq + 1);
      // Disallow setting home through the environment, because it should
      // always be resolved from the running Mesos binary (if any)
      if ((overwrite || !params.contains(key)) && key != "home") {
        params[key] = val;
      }
    }
    i++;
  }
}

void Configurator::loadCommandLine(int argc,
                                   char** argv,
                                   bool inferMesosHomeFromArg0,
                                   bool overwrite)
{
  // Set home based on argument 0 if asked to do so
  if (inferMesosHomeFromArg0) {
    // Copy argv[0] because dirname can modify it
    int lengthOfArg0 = strlen(argv[0]);
    char* copyOfArg0 = new char[lengthOfArg0 + 1];
    strncpy(copyOfArg0, argv[0], lengthOfArg0 + 1);
    // Get its directory, and then the parent of that directory
    string myDir = string(dirname(copyOfArg0));
    string parentDir = myDir + "/..";
    // Get the real name of this parent directory
    char path[PATH_MAX];
    if (realpath(parentDir.c_str(), path) == 0) {
      throw ConfigurationException(
        "Could not resolve MESOS_HOME from argv[0] -- realpath failed");
    }
    params["home"] = path;
    delete[] copyOfArg0;
  }

  // Convert args 1 and above to STL strings
  vector<string> args;
  for (int i=1; i < argc; i++) {
    args.push_back(string(argv[i]));
  }

  for (int i = 0; i < args.size(); i++) {
    string key, val;
    bool set = false;
    if (args[i].find("--", 0) == 0) {
      // handle "--" case
      size_t eq = args[i].find_first_of("=");
      if (eq == string::npos &&
          args[i].find("--no-", 0) == 0) { // handle --no-blah
        key = args[i].substr(5);
        val = "0";
        set = true;
        checkCommandLineParamFormat(key, true);
      } else if (eq == string::npos) {     // handle --blah
        key = args[i].substr(2);
        val = "1";
        set = true;
        checkCommandLineParamFormat(key, true);
      } else {                             // handle --blah=25
        key = args[i].substr(2, eq-2);
        val = args[i].substr(eq+1);
        set = true;
        checkCommandLineParamFormat(key, false);
      }
    } else if (args[i].find_first_of("-", 0) == 0 &&
               args[i].size() > 1) {
      // handle "-" case
      char shortName = '\0';
      if (args[i].find("-no-",0) == 0 && args[i].size() == 5) {
        shortName = args[i][4];
      } else if (args[i].size() == 2) {
        shortName = args[i][1];
      }
      if (shortName == '\0' || getLongName(shortName) == "") {
        string message = "Short option '" + args[i] + "' unrecognized ";
        throw ConfigurationException(message.c_str());
      }
      key = getLongName(shortName);
      if (args[i].find("-no-",0) == 0) { // handle -no-b
        val = "0";
        set = true;
        checkCommandLineParamFormat(key, true);
      } else if (options[key].validator->isBool() ||
                 i+1 == args.size() ) {  // handle -b
        val = "1";
        set = true;
        checkCommandLineParamFormat(key, true);
      } else {                           // handle -b 25
        val = args[i+1];
        set = true;
        i++;  // we've consumed next parameter as a "value"-parameter
      }
    }
    std::transform(key.begin(), key.end(), key.begin(), ::tolower);
    // Disallow setting "home" since it should only be inferred from
    // the location of the running Mesos binary (if any)
    if (set && (overwrite || !params.contains(key)) && key != "home") {
      params[key] = val;
    }
  }
}

void Configurator::loadConfigFile(const string& fname, bool overwrite)
{
  ifstream cfg(fname.c_str(), std::ios::in);
  if (!cfg.is_open()) {
    string message = "Couldn't read Mesos config file: " + fname;
    throw ConfigurationException(message.c_str());
  }

  string line, originalLine;

  while (!cfg.eof()) {
    getline(cfg, line);
    originalLine = line;
    // Strip any comment at end of line
    size_t hash = line.find_first_of("#"); // strip comments
    if (hash != string::npos) {
      line = line.substr(0, hash);
    }
    // Check for empty line
    line = StringUtils::trim(line);
    if (line == "") {
      continue;
    }
    // Split line by = and trim to get key and value
    vector<string> tokens;
    StringUtils::split(line, "=", &tokens);
    if (tokens.size() != 2) {
      string message = "Malformed line in config file: '" +
                       StringUtils::trim(originalLine) + "'";
      throw ConfigurationException(message.c_str());
    }
    string key = StringUtils::trim(tokens[0]);
    string value = StringUtils::trim(tokens[1]);
    // Disallow setting "home" since it should only be inferred from
    // the location of the running Mesos binary (if any)
    if ((overwrite || !params.contains(key)) && key != "home") {
      params[key] = value;
    }
  }
  cfg.close();
}

string Configurator::getUsage() const
{
  const int PAD = 5;
  string usage;

  map<string,string> col1; // key -> col 1 string
  int maxLen = 0;

  // construct string for the first column and get size of column
  foreachpair (const string& key, const Option& opt, options) {
    string val;
    if (opt.validator->isBool())
      val = "  --[no-]" + key;
    else
      val = "  --" + key + "=VAL";

    if (opt.hasShortName) {
      if (opt.validator->isBool())
        val += string(" (or -[no-]") + opt.shortName + ")";
      else
        val += string(" (or -") + opt.shortName + " VAL)";
    }

    col1[key] = val;
    maxLen = val.size() > maxLen ? val.size() : maxLen;
  }

  foreachpair (const string& key, const Option& opt, options) {
    string helpStr = opt.helpString;
    string line = col1[key];

    if (opt.defaultValue != "") {  // add default value
      // Place a space between help string and (default: VAL) if the
      // help string does not end with a newline itself
      size_t lastNewLine = helpStr.find_last_of("\n\r");
      if (helpStr.size() > 0 && lastNewLine != helpStr.size() - 1) {
        helpStr += " ";
      }
      string defval = opt.defaultValue;
      if (opt.validator->isBool())
        defval = opt.defaultValue == "0" ? "false" : "true";

      helpStr += "(default: " + defval + ")";
    }

    string pad(PAD + maxLen - line.size(), ' ');
    line += pad;
    size_t pos1 = 0, pos2 = 0;
    pos2 = helpStr.find_first_of("\n\r", pos1);
    line += helpStr.substr(pos1, pos2 - pos1) + "\n";
    usage += line;

    while(pos2 != string::npos) {  // handle multi line help strings
      line = "";
      pos1 = pos2 + 1;
      string pad2(PAD + maxLen, ' ');
      line += pad2;
      pos2 = helpStr.find_first_of("\n\r", pos1);
      line += helpStr.substr(pos1, pos2 - pos1) + "\n";
      usage += line;
    }

  }
  return usage;
}

void Configurator::loadDefaults()
{
  foreachpair (const string& key, const Option& option, options) {
    if (option.hasDefault && !params.contains(key)) {
      params[key] = option.defaultValue;
    }
  }
}

vector<string> Configurator::getOptions() const
{
  vector<string> ret;
  foreachpair (const string& key, _, options) {
    ret.push_back(key);
  }
  return ret;
}

Params& Configurator::getParams()
{
  return params;
}

string Configurator::getLongName(char shortName) const
{
  foreachpair (const string& key, const Option& opt, options) {
    if (opt.hasShortName && opt.shortName == shortName)
      return key;
  }
  return "";
}

void Configurator::clearMesosEnvironmentVars()
{
  char** environ = getEnviron();
  int i = 0;
  vector<string> toRemove;
  while (environ[i] != NULL) {
    string line = environ[i];
    if (line.find(ENV_VAR_PREFIX) == 0) {
      string key;
      size_t eq = line.find_first_of("=");
      if (eq == string::npos)
        continue; // ignore malformed lines (they shouldn't occur in environ!)
      key = line.substr(strlen(ENV_VAR_PREFIX), eq - strlen(ENV_VAR_PREFIX));
      toRemove.push_back(key);
    }
    i++;
  }
  foreach (string& str, toRemove) {
    unsetenv(str.c_str());
  }
}

void Configurator::checkCommandLineParamFormat(const string& key, bool gotBool)
  throw(ConfigurationException)
{
  if (options.find(key) != options.end() &&
      options[key].validator->isBool() != gotBool) {
    string message = "Option '" + key + "' should ";
    if (gotBool)
      message += "not ";
    message += "be a boolean.";

    throw ConfigurationException(message.c_str());
  }
}

void Configurator::dumpToGoogleLog()
{
  LOG(INFO) << "Dumping configuration options:";
  const map<string, string>& paramsMap = params.getMap();
  foreachpair (const string& key, const string& val, paramsMap) {
    LOG(INFO) << "  " << key << " = " << val;
  }
  LOG(INFO) << "End configuration dump";
}
<commit_msg>Fixed description of --conf option<commit_after>#include <libgen.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>

#include <algorithm>
#include <iostream>

#include "configurator.hpp"

#include "common/foreach.hpp"
#include "common/params.hpp"
#include "common/string_utils.hpp"

using namespace mesos::internal;

const char* Configurator::DEFAULT_CONFIG_DIR = "conf";
const char* Configurator::CONFIG_FILE_NAME = "mesos.conf";
const char* Configurator::ENV_VAR_PREFIX = "MESOS_";

// Define a function for accessing the list of environment variables
// in a platform-independent way.
// On Mac OS X, the environ symbol isn't visible to shared libraries,
// so we must use the _NSGetEnviron() function (see man environ on OS X).
// On other platforms, it's fine to access environ from shared libraries.
#ifdef __APPLE__
#include "crt_externs.h"
namespace {
char** getEnviron() { return *_NSGetEnviron(); }
}
#else
extern char** environ;
namespace {
char** getEnviron() { return environ; }
}
#endif /* __APPLE__ */

Configurator::Configurator()
{
  addOption<string>("conf",
                    "Specifies a config directory from which to\n"
                    "read Mesos config files. The Mesos binaries\n"
                    "use <install location>/conf by default");
}

void Configurator::validate()
{
  foreachpair (const string& key, const Option& opt, options) {
    if (params.contains(key) && opt.validator &&
        !opt.validator->isValid(params[key])) {
      string msg = "Invalid value for '" + key + "' option: " + params[key];
      throw ConfigurationException(msg.c_str());
    }
  }
}

Params& Configurator::load(int argc, char** argv, bool inferMesosHomeFromArg0)
{
  loadEnv();
  loadCommandLine(argc, argv, inferMesosHomeFromArg0);
  loadConfigFileIfGiven();
  loadDefaults();
  validate();
  return params;
}

Params& Configurator::load()
{
  loadEnv();
  loadConfigFileIfGiven();
  loadDefaults();
  validate();
  return params;
}

Params& Configurator::load(const map<string, string>& _params)
{
  loadEnv();
  params.loadMap(_params);
  loadConfigFileIfGiven();
  loadDefaults();
  validate();
  return params;
}

void Configurator::loadConfigFileIfGiven(bool overwrite) {
  if (params.contains("conf")) {
    // If conf param is given, always look for a config file in that directory
    string confDir = params["conf"];
    loadConfigFile(confDir + "/" + CONFIG_FILE_NAME, overwrite);
  } else if (params.contains("home")) {
    // Grab config file in MESOS_HOME/conf, if it exists
    string confDir = params["home"] + "/" + DEFAULT_CONFIG_DIR;
    string confFile = confDir + "/" + CONFIG_FILE_NAME;
    struct stat st;
    if (stat(confFile.c_str(), &st) == 0) {
      loadConfigFile(confFile, overwrite);
    }
  }
}

void Configurator::loadEnv(bool overwrite)
{
  char** environ = getEnviron();
  int i = 0;
  while (environ[i] != NULL) {
    string line = environ[i];
    if (line.find(ENV_VAR_PREFIX) == 0) {
      string key, val;
      size_t eq = line.find_first_of("=");
      if (eq == string::npos)
        continue; // ignore malformed lines (they shouldn't occur in environ!)
      key = line.substr(strlen(ENV_VAR_PREFIX), eq - strlen(ENV_VAR_PREFIX));
      std::transform(key.begin(), key.end(), key.begin(), ::tolower);
      val = line.substr(eq + 1);
      // Disallow setting home through the environment, because it should
      // always be resolved from the running Mesos binary (if any)
      if ((overwrite || !params.contains(key)) && key != "home") {
        params[key] = val;
      }
    }
    i++;
  }
}

void Configurator::loadCommandLine(int argc,
                                   char** argv,
                                   bool inferMesosHomeFromArg0,
                                   bool overwrite)
{
  // Set home based on argument 0 if asked to do so
  if (inferMesosHomeFromArg0) {
    // Copy argv[0] because dirname can modify it
    int lengthOfArg0 = strlen(argv[0]);
    char* copyOfArg0 = new char[lengthOfArg0 + 1];
    strncpy(copyOfArg0, argv[0], lengthOfArg0 + 1);
    // Get its directory, and then the parent of that directory
    string myDir = string(dirname(copyOfArg0));
    string parentDir = myDir + "/..";
    // Get the real name of this parent directory
    char path[PATH_MAX];
    if (realpath(parentDir.c_str(), path) == 0) {
      throw ConfigurationException(
        "Could not resolve MESOS_HOME from argv[0] -- realpath failed");
    }
    params["home"] = path;
    delete[] copyOfArg0;
  }

  // Convert args 1 and above to STL strings
  vector<string> args;
  for (int i=1; i < argc; i++) {
    args.push_back(string(argv[i]));
  }

  for (int i = 0; i < args.size(); i++) {
    string key, val;
    bool set = false;
    if (args[i].find("--", 0) == 0) {
      // handle "--" case
      size_t eq = args[i].find_first_of("=");
      if (eq == string::npos &&
          args[i].find("--no-", 0) == 0) { // handle --no-blah
        key = args[i].substr(5);
        val = "0";
        set = true;
        checkCommandLineParamFormat(key, true);
      } else if (eq == string::npos) {     // handle --blah
        key = args[i].substr(2);
        val = "1";
        set = true;
        checkCommandLineParamFormat(key, true);
      } else {                             // handle --blah=25
        key = args[i].substr(2, eq-2);
        val = args[i].substr(eq+1);
        set = true;
        checkCommandLineParamFormat(key, false);
      }
    } else if (args[i].find_first_of("-", 0) == 0 &&
               args[i].size() > 1) {
      // handle "-" case
      char shortName = '\0';
      if (args[i].find("-no-",0) == 0 && args[i].size() == 5) {
        shortName = args[i][4];
      } else if (args[i].size() == 2) {
        shortName = args[i][1];
      }
      if (shortName == '\0' || getLongName(shortName) == "") {
        string message = "Short option '" + args[i] + "' unrecognized ";
        throw ConfigurationException(message.c_str());
      }
      key = getLongName(shortName);
      if (args[i].find("-no-",0) == 0) { // handle -no-b
        val = "0";
        set = true;
        checkCommandLineParamFormat(key, true);
      } else if (options[key].validator->isBool() ||
                 i+1 == args.size() ) {  // handle -b
        val = "1";
        set = true;
        checkCommandLineParamFormat(key, true);
      } else {                           // handle -b 25
        val = args[i+1];
        set = true;
        i++;  // we've consumed next parameter as a "value"-parameter
      }
    }
    std::transform(key.begin(), key.end(), key.begin(), ::tolower);
    // Disallow setting "home" since it should only be inferred from
    // the location of the running Mesos binary (if any)
    if (set && (overwrite || !params.contains(key)) && key != "home") {
      params[key] = val;
    }
  }
}

void Configurator::loadConfigFile(const string& fname, bool overwrite)
{
  ifstream cfg(fname.c_str(), std::ios::in);
  if (!cfg.is_open()) {
    string message = "Couldn't read Mesos config file: " + fname;
    throw ConfigurationException(message.c_str());
  }

  string line, originalLine;

  while (!cfg.eof()) {
    getline(cfg, line);
    originalLine = line;
    // Strip any comment at end of line
    size_t hash = line.find_first_of("#"); // strip comments
    if (hash != string::npos) {
      line = line.substr(0, hash);
    }
    // Check for empty line
    line = StringUtils::trim(line);
    if (line == "") {
      continue;
    }
    // Split line by = and trim to get key and value
    vector<string> tokens;
    StringUtils::split(line, "=", &tokens);
    if (tokens.size() != 2) {
      string message = "Malformed line in config file: '" +
                       StringUtils::trim(originalLine) + "'";
      throw ConfigurationException(message.c_str());
    }
    string key = StringUtils::trim(tokens[0]);
    string value = StringUtils::trim(tokens[1]);
    // Disallow setting "home" since it should only be inferred from
    // the location of the running Mesos binary (if any)
    if ((overwrite || !params.contains(key)) && key != "home") {
      params[key] = value;
    }
  }
  cfg.close();
}

string Configurator::getUsage() const
{
  const int PAD = 5;
  string usage;

  map<string,string> col1; // key -> col 1 string
  int maxLen = 0;

  // construct string for the first column and get size of column
  foreachpair (const string& key, const Option& opt, options) {
    string val;
    if (opt.validator->isBool())
      val = "  --[no-]" + key;
    else
      val = "  --" + key + "=VAL";

    if (opt.hasShortName) {
      if (opt.validator->isBool())
        val += string(" (or -[no-]") + opt.shortName + ")";
      else
        val += string(" (or -") + opt.shortName + " VAL)";
    }

    col1[key] = val;
    maxLen = val.size() > maxLen ? val.size() : maxLen;
  }

  foreachpair (const string& key, const Option& opt, options) {
    string helpStr = opt.helpString;
    string line = col1[key];

    if (opt.defaultValue != "") {  // add default value
      // Place a space between help string and (default: VAL) if the
      // help string does not end with a newline itself
      size_t lastNewLine = helpStr.find_last_of("\n\r");
      if (helpStr.size() > 0 && lastNewLine != helpStr.size() - 1) {
        helpStr += " ";
      }
      string defval = opt.defaultValue;
      if (opt.validator->isBool())
        defval = opt.defaultValue == "0" ? "false" : "true";

      helpStr += "(default: " + defval + ")";
    }

    string pad(PAD + maxLen - line.size(), ' ');
    line += pad;
    size_t pos1 = 0, pos2 = 0;
    pos2 = helpStr.find_first_of("\n\r", pos1);
    line += helpStr.substr(pos1, pos2 - pos1) + "\n";
    usage += line;

    while(pos2 != string::npos) {  // handle multi line help strings
      line = "";
      pos1 = pos2 + 1;
      string pad2(PAD + maxLen, ' ');
      line += pad2;
      pos2 = helpStr.find_first_of("\n\r", pos1);
      line += helpStr.substr(pos1, pos2 - pos1) + "\n";
      usage += line;
    }

  }
  return usage;
}

void Configurator::loadDefaults()
{
  foreachpair (const string& key, const Option& option, options) {
    if (option.hasDefault && !params.contains(key)) {
      params[key] = option.defaultValue;
    }
  }
}

vector<string> Configurator::getOptions() const
{
  vector<string> ret;
  foreachpair (const string& key, _, options) {
    ret.push_back(key);
  }
  return ret;
}

Params& Configurator::getParams()
{
  return params;
}

string Configurator::getLongName(char shortName) const
{
  foreachpair (const string& key, const Option& opt, options) {
    if (opt.hasShortName && opt.shortName == shortName)
      return key;
  }
  return "";
}

void Configurator::clearMesosEnvironmentVars()
{
  char** environ = getEnviron();
  int i = 0;
  vector<string> toRemove;
  while (environ[i] != NULL) {
    string line = environ[i];
    if (line.find(ENV_VAR_PREFIX) == 0) {
      string key;
      size_t eq = line.find_first_of("=");
      if (eq == string::npos)
        continue; // ignore malformed lines (they shouldn't occur in environ!)
      key = line.substr(strlen(ENV_VAR_PREFIX), eq - strlen(ENV_VAR_PREFIX));
      toRemove.push_back(key);
    }
    i++;
  }
  foreach (string& str, toRemove) {
    unsetenv(str.c_str());
  }
}

void Configurator::checkCommandLineParamFormat(const string& key, bool gotBool)
  throw(ConfigurationException)
{
  if (options.find(key) != options.end() &&
      options[key].validator->isBool() != gotBool) {
    string message = "Option '" + key + "' should ";
    if (gotBool)
      message += "not ";
    message += "be a boolean.";

    throw ConfigurationException(message.c_str());
  }
}

void Configurator::dumpToGoogleLog()
{
  LOG(INFO) << "Dumping configuration options:";
  const map<string, string>& paramsMap = params.getMap();
  foreachpair (const string& key, const string& val, paramsMap) {
    LOG(INFO) << "  " << key << " = " << val;
  }
  LOG(INFO) << "End configuration dump";
}
<|endoftext|>


<commit_before>#include "UserClient_kext.hpp"
#include "CommonData.hpp"
#include "IOLockWrapper.hpp"

#define super IOUserClient

OSDefineMetaClassAndStructors(org_pqrs_driver_KeyRemap4MacBook_UserClient_kext, IOUserClient)

OSAsyncReference64 org_pqrs_driver_KeyRemap4MacBook_UserClient_kext::asyncref_;
bool org_pqrs_driver_KeyRemap4MacBook_UserClient_kext::notification_enabled_ = false;

IOExternalMethodDispatch org_pqrs_driver_KeyRemap4MacBook_UserClient_kext::methods_[BRIDGE_USERCLIENT__END__] = {
  { // BRIDGE_USERCLIENT_OPEN
    reinterpret_cast<IOExternalMethodAction>(&static_callback_open), // Method pointer.
    0,                                                               // No scalar input values.
    0,                                                               // No struct input value.
    0,                                                               // No scalar output values.
    0                                                                // No struct output value.
  },
  { // BRIDGE_USERCLIENT_CLOSE
    reinterpret_cast<IOExternalMethodAction>(&static_callback_close), // Method pointer.
    0,                                                         // No scalar input values.
    0,                                                         // No struct input value.
    0,                                                         // No scalar output values.
    0                                                          // No struct output value.
  },
  { // BRIDGE_USERCLIENT_SYNCHRONIZED_COMMUNICATION
    reinterpret_cast<IOExternalMethodAction>(&static_callback_synchronized_communication), // Method pointer.
    0,                                                                                // No scalar input values.
    sizeof(BridgeUserClientStruct),                                                        // The size of the input struct.
    1,                                                                                // One scalar output value.
    0,                                                                                // No struct output value.
  },
  { // BRIDGE_USERCLIENT_NOTIFICATION_FROM_KEXT
    reinterpret_cast<IOExternalMethodAction>(&static_callback_notification_from_kext), // Method pointer.
    0,                                                                                // No scalar input values.
    0,                                                                                // No struct input value.
    0,                                                                                // No scalar output values.
    0                                                                                // No struct output value.
  }
};

// ============================================================
// initWithTask is called as a result of the user process calling IOServiceOpen.
bool
org_pqrs_driver_KeyRemap4MacBook_UserClient_kext::initWithTask(task_t owningTask, void* securityToken, UInt32 type)
{
  if (clientHasPrivilege(owningTask, kIOClientPrivilegeLocalUser) != KERN_SUCCESS) {
    IOLOG_ERROR("UserClient_kext::initWithTask clientHasPrivilege failed\n");
    return false;
  }

  if (! super::initWithTask(owningTask, securityToken, type)) {
    IOLOG_ERROR("UserClient_kext::initWithTask super::initWithTask failed\n");
    return false;
  }

  task_     = owningTask;
  provider_ = NULL;

  // Don't change static values here. (For example, notification_enabled_)
  // initWithTask is called by each IOServiceOpen.
  //
  // If IOService is opened, other client will be failed.
  // Changing static values by other IOServiceOpen may destroy the current connection.

  return true;
}

// start is called after initWithTask as a result of the user process calling IOServiceOpen.
bool
org_pqrs_driver_KeyRemap4MacBook_UserClient_kext::start(IOService* provider)
{
  provider_ = OSDynamicCast(org_pqrs_driver_KeyRemap4MacBook, provider);
  if (! provider_) {
    IOLOG_ERROR("UserClient_kext::start provider == NULL\n");
    return false;
  }

  // It's important not to call super::start if some previous condition
  // (like an invalid provider) would cause this function to return false.
  // I/O Kit won't call stop on an object if its start function returned false.
  if (! super::start(provider)) {
    IOLOG_ERROR("UserClient_kext::start super::start failed\n");
    return false;
  }

  return true;
}

void
org_pqrs_driver_KeyRemap4MacBook_UserClient_kext::stop(IOService* provider)
{
  super::stop(provider);
  provider_ = NULL;
}

// clientClose is called as a result of the user process calling IOServiceClose.
IOReturn
org_pqrs_driver_KeyRemap4MacBook_UserClient_kext::clientClose(void)
{
  // Defensive coding in case the user process called IOServiceClose
  // without calling BRIDGE_USERCLIENT_CLOSE first.
  callback_close();

  // Inform the user process that this user client is no longer available. This will also cause the
  // user client instance to be destroyed.
  //
  // terminate would return false if the user process still had this user client open.
  // This should never happen in our case because this code path is only reached if the user process
  // explicitly requests closing the connection to the user client.
  if (! terminate()) {
    IOLOG_ERROR("UserClient_kext::clientClose terminate() failed\n");
  }

  // DON'T call super::clientClose, which just returns kIOReturnUnsupported.
  return kIOReturnSuccess;
}

bool
org_pqrs_driver_KeyRemap4MacBook_UserClient_kext::didTerminate(IOService* provider, IOOptionBits options, bool* defer)
{
  // If all pending I/O has been terminated, close our provider. If I/O is still outstanding, set defer to true
  // and the user client will not have stop called on it.
  callback_close();
  *defer = false;

  return super::didTerminate(provider, options, defer);
}

IOReturn
org_pqrs_driver_KeyRemap4MacBook_UserClient_kext::externalMethod(uint32_t selector, IOExternalMethodArguments* arguments,
                                                                 IOExternalMethodDispatch* dispatch, OSObject* target, void* reference)
{
  if (selector < static_cast<uint32_t>(BRIDGE_USERCLIENT__END__)) {
    dispatch = &(methods_[selector]);

    if (! target) {
      target = this;
    }
  }

  return super::externalMethod(selector, arguments, dispatch, target, reference);
}

// ======================================================================
IOReturn
org_pqrs_driver_KeyRemap4MacBook_UserClient_kext::static_callback_open(org_pqrs_driver_KeyRemap4MacBook_UserClient_kext* target, void* reference, IOExternalMethodArguments* arguments)
{
  if (! target) return kIOReturnBadArgument;
  return target->callback_open();
}

IOReturn
org_pqrs_driver_KeyRemap4MacBook_UserClient_kext::callback_open(void)
{
  if (provider_ == NULL || isInactive()) {
    // Return an error if we don't have a provider. This could happen if the user process
    // called callback_open without calling IOServiceOpen first. Or, the user client could be
    // in the process of being terminated and is thus inactive.
    IOLOG_ERROR("UserClient_kext::callback_open kIOReturnNotAttached\n");
    return kIOReturnNotAttached;
  }

  if (! provider_->open(this)) {
    // The most common reason this open call will fail is because the provider is already open
    // and it doesn't support being opened by more than one client at a time.
    IOLOG_ERROR("UserClient_kext::callback_open kIOReturnExclusiveAccess\n");
    return kIOReturnExclusiveAccess;
  }

  return kIOReturnSuccess;
}

// ----------------------------------------------------------------------
IOReturn
org_pqrs_driver_KeyRemap4MacBook_UserClient_kext::static_callback_close(org_pqrs_driver_KeyRemap4MacBook_UserClient_kext* target, void* reference, IOExternalMethodArguments* arguments)
{
  if (! target) return kIOReturnBadArgument;
  return target->callback_close();
}

IOReturn
org_pqrs_driver_KeyRemap4MacBook_UserClient_kext::callback_close(void)
{
  if (! provider_) {
    // Return an error if we don't have a provider. This could happen if the user process
    // called callback_close without calling IOServiceOpen first.
    IOLOG_ERROR("UserClient_kext::callback_close kIOReturnNotAttached\n");
    return kIOReturnNotAttached;
  }

  if (! provider_->isOpen(this)) {
    IOLOG_ERROR("UserClient_kext::callback_close kIOReturnNotOpen\n");
    return kIOReturnNotOpen;
  }

  notification_enabled_ = false;

  // Make sure we're the one who opened our provider before we tell it to close.
  provider_->close(this);

  return kIOReturnSuccess;
}

// ----------------------------------------------------------------------
IOReturn
org_pqrs_driver_KeyRemap4MacBook_UserClient_kext::static_callback_synchronized_communication(org_pqrs_driver_KeyRemap4MacBook_UserClient_kext* target, void* reference, IOExternalMethodArguments* arguments)
{
  if (! target) return kIOReturnBadArgument;
  return target->callback_synchronized_communication(static_cast<const BridgeUserClientStruct*>(arguments->structureInput), &arguments->scalarOutput[0]);
}

IOReturn
org_pqrs_driver_KeyRemap4MacBook_UserClient_kext::callback_synchronized_communication(const BridgeUserClientStruct* inputdata, uint64_t* outputdata)
{
  IOReturn result = kIOReturnSuccess;
  IOMemoryDescriptor* memorydescriptor = NULL;

  if (! inputdata || ! outputdata) {
    result = kIOReturnBadArgument;
    IOLOG_ERROR("UserClient_kext::callback_synchronized_communication kIOReturnBadArgument\n");
    goto finish;
  }

  if (provider_ == NULL || isInactive()) {
    // Return an error if we don't have a provider. This could happen if the user process
    // called callback_synchronized_communication without calling IOServiceOpen first.
    // Or, the user client could be in the process of being terminated and is thus inactive.
    result = kIOReturnNotAttached;
    IOLOG_ERROR("UserClient_kext::callback_synchronized_communication kIOReturnNotAttached\n");
    goto finish;
  }

  if (! provider_->isOpen(this)) {
    // Return an error if we do not have the driver open. This could happen if the user process
    // did not call callback_open before calling this function.
    result = kIOReturnNotOpen;
    IOLOG_ERROR("UserClient_kext::callback_synchronized_communication kIOReturnNotOpen\n");
    goto finish;
  }

  memorydescriptor = IOMemoryDescriptor::withAddressRange(inputdata->data, inputdata->size, kIODirectionNone, task_);
  if (! memorydescriptor) {
    result = kIOReturnVMError;
    IOLOG_ERROR("UserClient_kext::callback_synchronized_communication kIOReturnVMError\n");
    goto finish;
  }

  // wire it and make sure we can write it
  result = memorydescriptor->prepare(kIODirectionOutIn);
  if (kIOReturnSuccess != result) {
    IOLOG_ERROR("UserClient_kext::callback_synchronized_communication IOMemoryDescriptor::prepare failed(0x%x)\n", result);
    goto finish;
  }

  {
    // this map() will create a mapping in the users (the client of this IOUserClient) address space.
    IOMemoryMap* memorymap = memorydescriptor->map();
    if (! memorymap) {
      result = kIOReturnVMError;
      IOLOG_ERROR("UserClient_kext::callback_synchronized_communication IOMemoryDescriptor::map failed\n");

    } else {
      mach_vm_address_t address = memorymap->getAddress();
      handle_synchronized_communication(inputdata->type, address, inputdata->size, outputdata);
      memorymap->release();
    }
  }

  // Done with the I/O now.
  memorydescriptor->complete(kIODirectionOutIn);

finish:
  if (memorydescriptor) {
    memorydescriptor->release();
  }

  return result;
}

// ------------------------------------------------------------
IOReturn
org_pqrs_driver_KeyRemap4MacBook_UserClient_kext::static_callback_notification_from_kext(org_pqrs_driver_KeyRemap4MacBook_UserClient_kext* target, void* reference, IOExternalMethodArguments* arguments)
{
  if (! target) return kIOReturnBadArgument;
  return target->callback_notification_from_kext(arguments->asyncReference);
}

IOReturn
org_pqrs_driver_KeyRemap4MacBook_UserClient_kext::callback_notification_from_kext(OSAsyncReference64 asyncReference)
{
  if (provider_ == NULL || isInactive()) {
    // Return an error if we don't have a provider. This could happen if the user process
    // called callback_notification_from_kext without calling IOServiceOpen first.
    // Or, the user client could be in the process of being terminated and is thus inactive.
    IOLOG_ERROR("UserClient_kext::callback_notification_from_kext kIOReturnNotAttached\n");
    return kIOReturnNotAttached;
  }

  if (! provider_->isOpen(this)) {
    // Return an error if we do not have the driver open. This could happen if the user process
    // did not call callback_open before calling this function.
    IOLOG_ERROR("UserClient_kext::callback_notification_from_kext kIOReturnNotOpen\n");
    return kIOReturnNotOpen;
  }

  // ----------------------------------------
  bcopy(asyncReference, asyncref_, sizeof(OSAsyncReference64));
  notification_enabled_ = true;
  return kIOReturnSuccess;
}

void
org_pqrs_driver_KeyRemap4MacBook_UserClient_kext::send_notification_to_userspace(uint32_t type, uint32_t data)
{
  if (notification_enabled_) {
    io_user_reference_t args[] = { type, data };
    sendAsyncResult64(asyncref_, kIOReturnSuccess, args, 2);
  }
}

// ------------------------------------------------------------
void
org_pqrs_driver_KeyRemap4MacBook_UserClient_kext::handle_synchronized_communication(uint64_t type, mach_vm_address_t address, mach_vm_size_t size, uint64_t* outputdata)
{
  org_pqrs_KeyRemap4MacBook::IOLockWrapper::ScopedLock lk_eventlock(org_pqrs_KeyRemap4MacBook::CommonData::getEventLock());

  *outputdata = 1;

  switch (type) {
    case BRIDGE_USERCLIENT_TYPE_GET_STATUS_MESSAGE_EXTRA:
    case BRIDGE_USERCLIENT_TYPE_GET_STATUS_MESSAGE_MODIFIER:
    {
      int index = BRIDGE_USERCLIENT_NOTIFICATION_DATA_STATUS_MESSAGE_NONE;
      switch (type) {
        case BRIDGE_USERCLIENT_TYPE_GET_STATUS_MESSAGE_EXTRA:
          index = BRIDGE_USERCLIENT_NOTIFICATION_DATA_STATUS_MESSAGE_EXTRA;
          break;
        case BRIDGE_USERCLIENT_TYPE_GET_STATUS_MESSAGE_MODIFIER:
          index = BRIDGE_USERCLIENT_NOTIFICATION_DATA_STATUS_MESSAGE_MODIFIER;
          break;
      }

      if (index != BRIDGE_USERCLIENT_NOTIFICATION_DATA_STATUS_MESSAGE_NONE) {
        char* p = reinterpret_cast<char*>(address);
        if (p) {
          strlcpy(p, org_pqrs_KeyRemap4MacBook::CommonData::get_statusmessage(index), static_cast<size_t>(size));
          *outputdata = 0;
        }
      }
      break;
    }

    case BRIDGE_USERCLIENT_TYPE_SET_WORKSPACEDATA:
    {
      if (size == sizeof(BridgeWorkSpaceData)) {
        BridgeWorkSpaceData* p = reinterpret_cast<BridgeWorkSpaceData*>(address);
        if (p) {
          org_pqrs_KeyRemap4MacBook::CommonData::setcurrent_workspacedata(*p);
          *outputdata = 0;
        }
      }
      break;
    }
  }
}
<commit_msg>update kext around UserClient_kext<commit_after>#include "UserClient_kext.hpp"
#include "CommonData.hpp"
#include "IOLockWrapper.hpp"

#define super IOUserClient

OSDefineMetaClassAndStructors(org_pqrs_driver_KeyRemap4MacBook_UserClient_kext, IOUserClient)

OSAsyncReference64 org_pqrs_driver_KeyRemap4MacBook_UserClient_kext::asyncref_;
bool org_pqrs_driver_KeyRemap4MacBook_UserClient_kext::notification_enabled_ = false;

IOExternalMethodDispatch org_pqrs_driver_KeyRemap4MacBook_UserClient_kext::methods_[BRIDGE_USERCLIENT__END__] = {
  { // BRIDGE_USERCLIENT_OPEN
    reinterpret_cast<IOExternalMethodAction>(&static_callback_open), // Method pointer.
    0,                                                               // No scalar input values.
    0,                                                               // No struct input value.
    0,                                                               // No scalar output values.
    0                                                                // No struct output value.
  },
  { // BRIDGE_USERCLIENT_CLOSE
    reinterpret_cast<IOExternalMethodAction>(&static_callback_close), // Method pointer.
    0,                                                         // No scalar input values.
    0,                                                         // No struct input value.
    0,                                                         // No scalar output values.
    0                                                          // No struct output value.
  },
  { // BRIDGE_USERCLIENT_SYNCHRONIZED_COMMUNICATION
    reinterpret_cast<IOExternalMethodAction>(&static_callback_synchronized_communication), // Method pointer.
    0,                                                                                // No scalar input values.
    sizeof(BridgeUserClientStruct),                                                        // The size of the input struct.
    1,                                                                                // One scalar output value.
    0,                                                                                // No struct output value.
  },
  { // BRIDGE_USERCLIENT_NOTIFICATION_FROM_KEXT
    reinterpret_cast<IOExternalMethodAction>(&static_callback_notification_from_kext), // Method pointer.
    0,                                                                                // No scalar input values.
    0,                                                                                // No struct input value.
    0,                                                                                // No scalar output values.
    0                                                                                // No struct output value.
  }
};

// ============================================================
// initWithTask is called as a result of the user process calling IOServiceOpen.
bool
org_pqrs_driver_KeyRemap4MacBook_UserClient_kext::initWithTask(task_t owningTask, void* securityToken, UInt32 type)
{
  if (clientHasPrivilege(owningTask, kIOClientPrivilegeLocalUser) != KERN_SUCCESS) {
    IOLOG_ERROR("UserClient_kext::initWithTask clientHasPrivilege failed\n");
    return false;
  }

  if (! super::initWithTask(owningTask, securityToken, type)) {
    IOLOG_ERROR("UserClient_kext::initWithTask super::initWithTask failed\n");
    return false;
  }

  task_     = owningTask;
  provider_ = NULL;

  // Don't change static values here. (For example, notification_enabled_)
  // initWithTask is called by each IOServiceOpen.
  //
  // If IOService is opened, other client will be failed.
  // Changing static values by other IOServiceOpen may destroy the current connection.

  return true;
}

// start is called after initWithTask as a result of the user process calling IOServiceOpen.
bool
org_pqrs_driver_KeyRemap4MacBook_UserClient_kext::start(IOService* provider)
{
  provider_ = OSDynamicCast(org_pqrs_driver_KeyRemap4MacBook, provider);
  if (! provider_) {
    IOLOG_ERROR("UserClient_kext::start provider == NULL\n");
    return false;
  }

  // It's important not to call super::start if some previous condition
  // (like an invalid provider) would cause this function to return false.
  // I/O Kit won't call stop on an object if its start function returned false.
  if (! super::start(provider)) {
    IOLOG_ERROR("UserClient_kext::start super::start failed\n");
    return false;
  }

  return true;
}

void
org_pqrs_driver_KeyRemap4MacBook_UserClient_kext::stop(IOService* provider)
{
  super::stop(provider);
  provider_ = NULL;
}

// clientClose is called as a result of the user process calling IOServiceClose.
IOReturn
org_pqrs_driver_KeyRemap4MacBook_UserClient_kext::clientClose(void)
{
  // Defensive coding in case the user process called IOServiceClose
  // without calling BRIDGE_USERCLIENT_CLOSE first.
  callback_close();

  // Inform the user process that this user client is no longer available. This will also cause the
  // user client instance to be destroyed.
  //
  // terminate would return false if the user process still had this user client open.
  // This should never happen in our case because this code path is only reached if the user process
  // explicitly requests closing the connection to the user client.
  if (! terminate()) {
    IOLOG_ERROR("UserClient_kext::clientClose terminate() failed\n");
  }

  // DON'T call super::clientClose, which just returns kIOReturnUnsupported.
  return kIOReturnSuccess;
}

bool
org_pqrs_driver_KeyRemap4MacBook_UserClient_kext::didTerminate(IOService* provider, IOOptionBits options, bool* defer)
{
  // If all pending I/O has been terminated, close our provider. If I/O is still outstanding, set defer to true
  // and the user client will not have stop called on it.
  callback_close();
  *defer = false;

  return super::didTerminate(provider, options, defer);
}

IOReturn
org_pqrs_driver_KeyRemap4MacBook_UserClient_kext::externalMethod(uint32_t selector, IOExternalMethodArguments* arguments,
                                                                 IOExternalMethodDispatch* dispatch, OSObject* target, void* reference)
{
  if (selector < static_cast<uint32_t>(BRIDGE_USERCLIENT__END__)) {
    dispatch = &(methods_[selector]);

    if (! target) {
      target = this;
    }
  }

  return super::externalMethod(selector, arguments, dispatch, target, reference);
}

// ======================================================================
IOReturn
org_pqrs_driver_KeyRemap4MacBook_UserClient_kext::static_callback_open(org_pqrs_driver_KeyRemap4MacBook_UserClient_kext* target, void* reference, IOExternalMethodArguments* arguments)
{
  if (! target) return kIOReturnBadArgument;
  return target->callback_open();
}

IOReturn
org_pqrs_driver_KeyRemap4MacBook_UserClient_kext::callback_open(void)
{
  if (provider_ == NULL || isInactive()) {
    // Return an error if we don't have a provider. This could happen if the user process
    // called callback_open without calling IOServiceOpen first. Or, the user client could be
    // in the process of being terminated and is thus inactive.
    IOLOG_ERROR("UserClient_kext::callback_open kIOReturnNotAttached\n");
    return kIOReturnNotAttached;
  }

  if (! provider_->open(this)) {
    // The most common reason this open call will fail is because the provider is already open
    // and it doesn't support being opened by more than one client at a time.
    IOLOG_ERROR("UserClient_kext::callback_open kIOReturnExclusiveAccess\n");
    return kIOReturnExclusiveAccess;
  }

  return kIOReturnSuccess;
}

// ----------------------------------------------------------------------
IOReturn
org_pqrs_driver_KeyRemap4MacBook_UserClient_kext::static_callback_close(org_pqrs_driver_KeyRemap4MacBook_UserClient_kext* target, void* reference, IOExternalMethodArguments* arguments)
{
  if (! target) return kIOReturnBadArgument;
  return target->callback_close();
}

IOReturn
org_pqrs_driver_KeyRemap4MacBook_UserClient_kext::callback_close(void)
{
  if (! provider_) {
    // Return an error if we don't have a provider. This could happen if the user process
    // called callback_close without calling IOServiceOpen first.
    IOLOG_ERROR("UserClient_kext::callback_close kIOReturnNotAttached\n");
    return kIOReturnNotAttached;
  }

  if (! provider_->isOpen(this)) {
    // This error can occur in the normal usage. (by fast user switching)
    // So we suppress the log message.
#if 0
    IOLOG_ERROR("UserClient_kext::callback_close kIOReturnNotOpen\n");
#endif
    return kIOReturnNotOpen;
  }

  notification_enabled_ = false;

  // Make sure we're the one who opened our provider before we tell it to close.
  provider_->close(this);

  return kIOReturnSuccess;
}

// ----------------------------------------------------------------------
IOReturn
org_pqrs_driver_KeyRemap4MacBook_UserClient_kext::static_callback_synchronized_communication(org_pqrs_driver_KeyRemap4MacBook_UserClient_kext* target, void* reference, IOExternalMethodArguments* arguments)
{
  if (! target) return kIOReturnBadArgument;
  return target->callback_synchronized_communication(static_cast<const BridgeUserClientStruct*>(arguments->structureInput), &arguments->scalarOutput[0]);
}

IOReturn
org_pqrs_driver_KeyRemap4MacBook_UserClient_kext::callback_synchronized_communication(const BridgeUserClientStruct* inputdata, uint64_t* outputdata)
{
  IOReturn result = kIOReturnSuccess;
  IOMemoryDescriptor* memorydescriptor = NULL;

  if (! inputdata || ! outputdata) {
    result = kIOReturnBadArgument;
    IOLOG_ERROR("UserClient_kext::callback_synchronized_communication kIOReturnBadArgument\n");
    goto finish;
  }

  if (provider_ == NULL || isInactive()) {
    // Return an error if we don't have a provider. This could happen if the user process
    // called callback_synchronized_communication without calling IOServiceOpen first.
    // Or, the user client could be in the process of being terminated and is thus inactive.
    result = kIOReturnNotAttached;
    IOLOG_ERROR("UserClient_kext::callback_synchronized_communication kIOReturnNotAttached\n");
    goto finish;
  }

  if (! provider_->isOpen(this)) {
    // Return an error if we do not have the driver open. This could happen if the user process
    // did not call callback_open before calling this function.
    result = kIOReturnNotOpen;
    IOLOG_ERROR("UserClient_kext::callback_synchronized_communication kIOReturnNotOpen\n");
    goto finish;
  }

  memorydescriptor = IOMemoryDescriptor::withAddressRange(inputdata->data, inputdata->size, kIODirectionNone, task_);
  if (! memorydescriptor) {
    result = kIOReturnVMError;
    IOLOG_ERROR("UserClient_kext::callback_synchronized_communication kIOReturnVMError\n");
    goto finish;
  }

  // wire it and make sure we can write it
  result = memorydescriptor->prepare(kIODirectionOutIn);
  if (kIOReturnSuccess != result) {
    IOLOG_ERROR("UserClient_kext::callback_synchronized_communication IOMemoryDescriptor::prepare failed(0x%x)\n", result);
    goto finish;
  }

  {
    // this map() will create a mapping in the users (the client of this IOUserClient) address space.
    IOMemoryMap* memorymap = memorydescriptor->map();
    if (! memorymap) {
      result = kIOReturnVMError;
      IOLOG_ERROR("UserClient_kext::callback_synchronized_communication IOMemoryDescriptor::map failed\n");

    } else {
      mach_vm_address_t address = memorymap->getAddress();
      handle_synchronized_communication(inputdata->type, address, inputdata->size, outputdata);
      memorymap->release();
    }
  }

  // Done with the I/O now.
  memorydescriptor->complete(kIODirectionOutIn);

finish:
  if (memorydescriptor) {
    memorydescriptor->release();
  }

  return result;
}

// ------------------------------------------------------------
IOReturn
org_pqrs_driver_KeyRemap4MacBook_UserClient_kext::static_callback_notification_from_kext(org_pqrs_driver_KeyRemap4MacBook_UserClient_kext* target, void* reference, IOExternalMethodArguments* arguments)
{
  if (! target) return kIOReturnBadArgument;
  return target->callback_notification_from_kext(arguments->asyncReference);
}

IOReturn
org_pqrs_driver_KeyRemap4MacBook_UserClient_kext::callback_notification_from_kext(OSAsyncReference64 asyncReference)
{
  if (provider_ == NULL || isInactive()) {
    // Return an error if we don't have a provider. This could happen if the user process
    // called callback_notification_from_kext without calling IOServiceOpen first.
    // Or, the user client could be in the process of being terminated and is thus inactive.
    IOLOG_ERROR("UserClient_kext::callback_notification_from_kext kIOReturnNotAttached\n");
    return kIOReturnNotAttached;
  }

  if (! provider_->isOpen(this)) {
    // Return an error if we do not have the driver open. This could happen if the user process
    // did not call callback_open before calling this function.
    IOLOG_ERROR("UserClient_kext::callback_notification_from_kext kIOReturnNotOpen\n");
    return kIOReturnNotOpen;
  }

  // ----------------------------------------
  bcopy(asyncReference, asyncref_, sizeof(OSAsyncReference64));
  notification_enabled_ = true;
  return kIOReturnSuccess;
}

void
org_pqrs_driver_KeyRemap4MacBook_UserClient_kext::send_notification_to_userspace(uint32_t type, uint32_t data)
{
  if (notification_enabled_) {
    io_user_reference_t args[] = { type, data };
    sendAsyncResult64(asyncref_, kIOReturnSuccess, args, 2);
  }
}

// ------------------------------------------------------------
void
org_pqrs_driver_KeyRemap4MacBook_UserClient_kext::handle_synchronized_communication(uint64_t type, mach_vm_address_t address, mach_vm_size_t size, uint64_t* outputdata)
{
  org_pqrs_KeyRemap4MacBook::IOLockWrapper::ScopedLock lk_eventlock(org_pqrs_KeyRemap4MacBook::CommonData::getEventLock());

  *outputdata = 1;

  switch (type) {
    case BRIDGE_USERCLIENT_TYPE_GET_STATUS_MESSAGE_EXTRA:
    case BRIDGE_USERCLIENT_TYPE_GET_STATUS_MESSAGE_MODIFIER:
    {
      int index = BRIDGE_USERCLIENT_NOTIFICATION_DATA_STATUS_MESSAGE_NONE;
      switch (type) {
        case BRIDGE_USERCLIENT_TYPE_GET_STATUS_MESSAGE_EXTRA:
          index = BRIDGE_USERCLIENT_NOTIFICATION_DATA_STATUS_MESSAGE_EXTRA;
          break;
        case BRIDGE_USERCLIENT_TYPE_GET_STATUS_MESSAGE_MODIFIER:
          index = BRIDGE_USERCLIENT_NOTIFICATION_DATA_STATUS_MESSAGE_MODIFIER;
          break;
      }

      if (index != BRIDGE_USERCLIENT_NOTIFICATION_DATA_STATUS_MESSAGE_NONE) {
        char* p = reinterpret_cast<char*>(address);
        if (p) {
          strlcpy(p, org_pqrs_KeyRemap4MacBook::CommonData::get_statusmessage(index), static_cast<size_t>(size));
          *outputdata = 0;
        }
      }
      break;
    }

    case BRIDGE_USERCLIENT_TYPE_SET_WORKSPACEDATA:
    {
      if (size == sizeof(BridgeWorkSpaceData)) {
        BridgeWorkSpaceData* p = reinterpret_cast<BridgeWorkSpaceData*>(address);
        if (p) {
          org_pqrs_KeyRemap4MacBook::CommonData::setcurrent_workspacedata(*p);
          *outputdata = 0;
        }
      }
      break;
    }
  }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2004-2006 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Kevin Lim
 *          Korey Sewell
 */

#include "arch/registers.hh"
#include "config/the_isa.hh"
#include "cpu/o3/thread_context.hh"
#include "cpu/quiesce_event.hh"

#if FULL_SYSTEM
template <class Impl>
VirtualPort *
O3ThreadContext<Impl>::getVirtPort()
{
    return thread->getVirtPort();
}

template <class Impl>
void
O3ThreadContext<Impl>::dumpFuncProfile()
{
    thread->dumpFuncProfile();
}
#endif

template <class Impl>
void
O3ThreadContext<Impl>::takeOverFrom(ThreadContext *old_context)
{
    // some things should already be set up
#if FULL_SYSTEM
    assert(getSystemPtr() == old_context->getSystemPtr());
#else
    assert(getProcessPtr() == old_context->getProcessPtr());
#endif

    // copy over functional state
    setStatus(old_context->status());
    copyArchRegs(old_context);
    setContextId(old_context->contextId());
    setThreadId(old_context->threadId());

#if !FULL_SYSTEM
    thread->funcExeInst = old_context->readFuncExeInst();
#else
    EndQuiesceEvent *other_quiesce = old_context->getQuiesceEvent();
    if (other_quiesce) {
        // Point the quiesce event's TC at this TC so that it wakes up
        // the proper CPU.
        other_quiesce->tc = this;
    }
    if (thread->quiesceEvent) {
        thread->quiesceEvent->tc = this;
    }

    // Transfer kernel stats from one CPU to the other.
    thread->kernelStats = old_context->getKernelStats();
//    storeCondFailures = 0;
    cpu->lockFlag = false;
#endif

    old_context->setStatus(ThreadContext::Halted);

    thread->inSyscall = false;
    thread->trapPending = false;
}

template <class Impl>
void
O3ThreadContext<Impl>::activate(int delay)
{
    DPRINTF(O3CPU, "Calling activate on Thread Context %d\n",
            threadId());

    if (thread->status() == ThreadContext::Active)
        return;

#if FULL_SYSTEM
    thread->lastActivate = curTick;
#endif

    thread->setStatus(ThreadContext::Active);

    // status() == Suspended
    cpu->activateContext(thread->threadId(), delay);
}

template <class Impl>
void
O3ThreadContext<Impl>::suspend(int delay)
{
    DPRINTF(O3CPU, "Calling suspend on Thread Context %d\n",
            threadId());

    if (thread->status() == ThreadContext::Suspended)
        return;

#if FULL_SYSTEM
    thread->lastActivate = curTick;
    thread->lastSuspend = curTick;
#endif
/*
#if FULL_SYSTEM
    // Don't change the status from active if there are pending interrupts
    if (cpu->checkInterrupts()) {
        assert(status() == ThreadContext::Active);
        return;
    }
#endif
*/
    thread->setStatus(ThreadContext::Suspended);
    cpu->suspendContext(thread->threadId());
}

template <class Impl>
void
O3ThreadContext<Impl>::halt(int delay)
{
    DPRINTF(O3CPU, "Calling halt on Thread Context %d\n",
            threadId());

    if (thread->status() == ThreadContext::Halted)
        return;

    thread->setStatus(ThreadContext::Halted);
    cpu->haltContext(thread->threadId());
}

template <class Impl>
void
O3ThreadContext<Impl>::regStats(const std::string &name)
{
#if FULL_SYSTEM
    thread->kernelStats = new TheISA::Kernel::Statistics(cpu->system);
    thread->kernelStats->regStats(name + ".kern");
#endif
}

template <class Impl>
void
O3ThreadContext<Impl>::serialize(std::ostream &os)
{
#if FULL_SYSTEM
    if (thread->kernelStats)
        thread->kernelStats->serialize(os);
#endif

}

template <class Impl>
void
O3ThreadContext<Impl>::unserialize(Checkpoint *cp, const std::string &section)
{
#if FULL_SYSTEM
    if (thread->kernelStats)
        thread->kernelStats->unserialize(cp, section);
#endif

}

#if FULL_SYSTEM
template <class Impl>
Tick
O3ThreadContext<Impl>::readLastActivate()
{
    return thread->lastActivate;
}

template <class Impl>
Tick
O3ThreadContext<Impl>::readLastSuspend()
{
    return thread->lastSuspend;
}

template <class Impl>
void
O3ThreadContext<Impl>::profileClear()
{
    thread->profileClear();
}

template <class Impl>
void
O3ThreadContext<Impl>::profileSample()
{
    thread->profileSample();
}
#endif

template <class Impl>
void
O3ThreadContext<Impl>::copyArchRegs(ThreadContext *tc)
{
    // This function will mess things up unless the ROB is empty and
    // there are no instructions in the pipeline.
    ThreadID tid = thread->threadId();
    PhysRegIndex renamed_reg;

    // First loop through the integer registers.
    for (int i = 0; i < TheISA::NumIntRegs; ++i) {
        renamed_reg = cpu->renameMap[tid].lookup(i);

        DPRINTF(O3CPU, "Copying over register %i, had data %lli, "
                "now has data %lli.\n",
                renamed_reg, cpu->readIntReg(renamed_reg),
                tc->readIntReg(i));

        cpu->setIntReg(renamed_reg, tc->readIntReg(i));
    }

    // Then loop through the floating point registers.
    for (int i = 0; i < TheISA::NumFloatRegs; ++i) {
        renamed_reg = cpu->renameMap[tid].lookup(i + TheISA::FP_Base_DepTag);
        cpu->setFloatRegBits(renamed_reg,
                             tc->readFloatRegBits(i));
    }

    // Copy the misc regs.
    TheISA::copyMiscRegs(tc, this);

    // Then finally set the PC, the next PC, the nextNPC, the micropc, and the
    // next micropc.
    cpu->pcState(tc->pcState(), tid);
#if !FULL_SYSTEM
    this->thread->funcExeInst = tc->readFuncExeInst();
#endif
}

template <class Impl>
void
O3ThreadContext<Impl>::clearArchRegs()
{}

template <class Impl>
uint64_t
O3ThreadContext<Impl>::readIntReg(int reg_idx)
{
    reg_idx = cpu->isa[thread->threadId()].flattenIntIndex(reg_idx);
    return cpu->readArchIntReg(reg_idx, thread->threadId());
}

template <class Impl>
TheISA::FloatReg
O3ThreadContext<Impl>::readFloatReg(int reg_idx)
{
    reg_idx = cpu->isa[thread->threadId()].flattenFloatIndex(reg_idx);
    return cpu->readArchFloatReg(reg_idx, thread->threadId());
}

template <class Impl>
TheISA::FloatRegBits
O3ThreadContext<Impl>::readFloatRegBits(int reg_idx)
{
    reg_idx = cpu->isa[thread->threadId()].flattenFloatIndex(reg_idx);
    return cpu->readArchFloatRegInt(reg_idx, thread->threadId());
}

template <class Impl>
void
O3ThreadContext<Impl>::setIntReg(int reg_idx, uint64_t val)
{
    reg_idx = cpu->isa[thread->threadId()].flattenIntIndex(reg_idx);
    cpu->setArchIntReg(reg_idx, val, thread->threadId());

    // Squash if we're not already in a state update mode.
    if (!thread->trapPending && !thread->inSyscall) {
        cpu->squashFromTC(thread->threadId());
    }
}

template <class Impl>
void
O3ThreadContext<Impl>::setFloatReg(int reg_idx, FloatReg val)
{
    reg_idx = cpu->isa[thread->threadId()].flattenFloatIndex(reg_idx);
    cpu->setArchFloatReg(reg_idx, val, thread->threadId());

    if (!thread->trapPending && !thread->inSyscall) {
        cpu->squashFromTC(thread->threadId());
    }
}

template <class Impl>
void
O3ThreadContext<Impl>::setFloatRegBits(int reg_idx, FloatRegBits val)
{
    reg_idx = cpu->isa[thread->threadId()].flattenFloatIndex(reg_idx);
    cpu->setArchFloatRegInt(reg_idx, val, thread->threadId());

    // Squash if we're not already in a state update mode.
    if (!thread->trapPending && !thread->inSyscall) {
        cpu->squashFromTC(thread->threadId());
    }
}

template <class Impl>
void
O3ThreadContext<Impl>::pcState(const TheISA::PCState &val)
{
    cpu->pcState(val, thread->threadId());

    // Squash if we're not already in a state update mode.
    if (!thread->trapPending && !thread->inSyscall) {
        cpu->squashFromTC(thread->threadId());
    }
}

template <class Impl>
int
O3ThreadContext<Impl>::flattenIntIndex(int reg)
{
    return cpu->isa[thread->threadId()].flattenIntIndex(reg);
}

template <class Impl>
int
O3ThreadContext<Impl>::flattenFloatIndex(int reg)
{
    return cpu->isa[thread->threadId()].flattenFloatIndex(reg);
}

template <class Impl>
void
O3ThreadContext<Impl>::setMiscRegNoEffect(int misc_reg, const MiscReg &val)
{
    cpu->setMiscRegNoEffect(misc_reg, val, thread->threadId());

    // Squash if we're not already in a state update mode.
    if (!thread->trapPending && !thread->inSyscall) {
        cpu->squashFromTC(thread->threadId());
    }
}

template <class Impl>
void
O3ThreadContext<Impl>::setMiscReg(int misc_reg,
                                                const MiscReg &val)
{
    cpu->setMiscReg(misc_reg, val, thread->threadId());

    // Squash if we're not already in a state update mode.
    if (!thread->trapPending && !thread->inSyscall) {
        cpu->squashFromTC(thread->threadId());
    }
}

<commit_msg>O3: reset architetural state by calling clear()<commit_after>/*
 * Copyright (c) 2010 ARM Limited
 * All rights reserved
 *
 * The license below extends only to copyright in the software and shall
 * not be construed as granting a license to any other intellectual
 * property including but not limited to intellectual property relating
 * to a hardware implementation of the functionality of the software
 * licensed hereunder.  You may use the software subject to the license
 * terms below provided that you ensure that this notice is replicated
 * unmodified and in its entirety in all distributions of the software,
 * modified or unmodified, in source code or in binary form.
 *
 * Copyright (c) 2004-2006 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Kevin Lim
 *          Korey Sewell
 */

#include "arch/registers.hh"
#include "config/the_isa.hh"
#include "cpu/o3/thread_context.hh"
#include "cpu/quiesce_event.hh"

#if FULL_SYSTEM
template <class Impl>
VirtualPort *
O3ThreadContext<Impl>::getVirtPort()
{
    return thread->getVirtPort();
}

template <class Impl>
void
O3ThreadContext<Impl>::dumpFuncProfile()
{
    thread->dumpFuncProfile();
}
#endif

template <class Impl>
void
O3ThreadContext<Impl>::takeOverFrom(ThreadContext *old_context)
{
    // some things should already be set up
#if FULL_SYSTEM
    assert(getSystemPtr() == old_context->getSystemPtr());
#else
    assert(getProcessPtr() == old_context->getProcessPtr());
#endif

    // copy over functional state
    setStatus(old_context->status());
    copyArchRegs(old_context);
    setContextId(old_context->contextId());
    setThreadId(old_context->threadId());

#if !FULL_SYSTEM
    thread->funcExeInst = old_context->readFuncExeInst();
#else
    EndQuiesceEvent *other_quiesce = old_context->getQuiesceEvent();
    if (other_quiesce) {
        // Point the quiesce event's TC at this TC so that it wakes up
        // the proper CPU.
        other_quiesce->tc = this;
    }
    if (thread->quiesceEvent) {
        thread->quiesceEvent->tc = this;
    }

    // Transfer kernel stats from one CPU to the other.
    thread->kernelStats = old_context->getKernelStats();
//    storeCondFailures = 0;
    cpu->lockFlag = false;
#endif

    old_context->setStatus(ThreadContext::Halted);

    thread->inSyscall = false;
    thread->trapPending = false;
}

template <class Impl>
void
O3ThreadContext<Impl>::activate(int delay)
{
    DPRINTF(O3CPU, "Calling activate on Thread Context %d\n",
            threadId());

    if (thread->status() == ThreadContext::Active)
        return;

#if FULL_SYSTEM
    thread->lastActivate = curTick;
#endif

    thread->setStatus(ThreadContext::Active);

    // status() == Suspended
    cpu->activateContext(thread->threadId(), delay);
}

template <class Impl>
void
O3ThreadContext<Impl>::suspend(int delay)
{
    DPRINTF(O3CPU, "Calling suspend on Thread Context %d\n",
            threadId());

    if (thread->status() == ThreadContext::Suspended)
        return;

#if FULL_SYSTEM
    thread->lastActivate = curTick;
    thread->lastSuspend = curTick;
#endif
/*
#if FULL_SYSTEM
    // Don't change the status from active if there are pending interrupts
    if (cpu->checkInterrupts()) {
        assert(status() == ThreadContext::Active);
        return;
    }
#endif
*/
    thread->setStatus(ThreadContext::Suspended);
    cpu->suspendContext(thread->threadId());
}

template <class Impl>
void
O3ThreadContext<Impl>::halt(int delay)
{
    DPRINTF(O3CPU, "Calling halt on Thread Context %d\n",
            threadId());

    if (thread->status() == ThreadContext::Halted)
        return;

    thread->setStatus(ThreadContext::Halted);
    cpu->haltContext(thread->threadId());
}

template <class Impl>
void
O3ThreadContext<Impl>::regStats(const std::string &name)
{
#if FULL_SYSTEM
    thread->kernelStats = new TheISA::Kernel::Statistics(cpu->system);
    thread->kernelStats->regStats(name + ".kern");
#endif
}

template <class Impl>
void
O3ThreadContext<Impl>::serialize(std::ostream &os)
{
#if FULL_SYSTEM
    if (thread->kernelStats)
        thread->kernelStats->serialize(os);
#endif

}

template <class Impl>
void
O3ThreadContext<Impl>::unserialize(Checkpoint *cp, const std::string &section)
{
#if FULL_SYSTEM
    if (thread->kernelStats)
        thread->kernelStats->unserialize(cp, section);
#endif

}

#if FULL_SYSTEM
template <class Impl>
Tick
O3ThreadContext<Impl>::readLastActivate()
{
    return thread->lastActivate;
}

template <class Impl>
Tick
O3ThreadContext<Impl>::readLastSuspend()
{
    return thread->lastSuspend;
}

template <class Impl>
void
O3ThreadContext<Impl>::profileClear()
{
    thread->profileClear();
}

template <class Impl>
void
O3ThreadContext<Impl>::profileSample()
{
    thread->profileSample();
}
#endif

template <class Impl>
void
O3ThreadContext<Impl>::copyArchRegs(ThreadContext *tc)
{
    // This function will mess things up unless the ROB is empty and
    // there are no instructions in the pipeline.
    ThreadID tid = thread->threadId();
    PhysRegIndex renamed_reg;

    // First loop through the integer registers.
    for (int i = 0; i < TheISA::NumIntRegs; ++i) {
        renamed_reg = cpu->renameMap[tid].lookup(i);

        DPRINTF(O3CPU, "Copying over register %i, had data %lli, "
                "now has data %lli.\n",
                renamed_reg, cpu->readIntReg(renamed_reg),
                tc->readIntReg(i));

        cpu->setIntReg(renamed_reg, tc->readIntReg(i));
    }

    // Then loop through the floating point registers.
    for (int i = 0; i < TheISA::NumFloatRegs; ++i) {
        renamed_reg = cpu->renameMap[tid].lookup(i + TheISA::FP_Base_DepTag);
        cpu->setFloatRegBits(renamed_reg,
                             tc->readFloatRegBits(i));
    }

    // Copy the misc regs.
    TheISA::copyMiscRegs(tc, this);

    // Then finally set the PC, the next PC, the nextNPC, the micropc, and the
    // next micropc.
    cpu->pcState(tc->pcState(), tid);
#if !FULL_SYSTEM
    this->thread->funcExeInst = tc->readFuncExeInst();
#endif
}

template <class Impl>
void
O3ThreadContext<Impl>::clearArchRegs()
{
    cpu->isa[thread->threadId()].clear();
}

template <class Impl>
uint64_t
O3ThreadContext<Impl>::readIntReg(int reg_idx)
{
    reg_idx = cpu->isa[thread->threadId()].flattenIntIndex(reg_idx);
    return cpu->readArchIntReg(reg_idx, thread->threadId());
}

template <class Impl>
TheISA::FloatReg
O3ThreadContext<Impl>::readFloatReg(int reg_idx)
{
    reg_idx = cpu->isa[thread->threadId()].flattenFloatIndex(reg_idx);
    return cpu->readArchFloatReg(reg_idx, thread->threadId());
}

template <class Impl>
TheISA::FloatRegBits
O3ThreadContext<Impl>::readFloatRegBits(int reg_idx)
{
    reg_idx = cpu->isa[thread->threadId()].flattenFloatIndex(reg_idx);
    return cpu->readArchFloatRegInt(reg_idx, thread->threadId());
}

template <class Impl>
void
O3ThreadContext<Impl>::setIntReg(int reg_idx, uint64_t val)
{
    reg_idx = cpu->isa[thread->threadId()].flattenIntIndex(reg_idx);
    cpu->setArchIntReg(reg_idx, val, thread->threadId());

    // Squash if we're not already in a state update mode.
    if (!thread->trapPending && !thread->inSyscall) {
        cpu->squashFromTC(thread->threadId());
    }
}

template <class Impl>
void
O3ThreadContext<Impl>::setFloatReg(int reg_idx, FloatReg val)
{
    reg_idx = cpu->isa[thread->threadId()].flattenFloatIndex(reg_idx);
    cpu->setArchFloatReg(reg_idx, val, thread->threadId());

    if (!thread->trapPending && !thread->inSyscall) {
        cpu->squashFromTC(thread->threadId());
    }
}

template <class Impl>
void
O3ThreadContext<Impl>::setFloatRegBits(int reg_idx, FloatRegBits val)
{
    reg_idx = cpu->isa[thread->threadId()].flattenFloatIndex(reg_idx);
    cpu->setArchFloatRegInt(reg_idx, val, thread->threadId());

    // Squash if we're not already in a state update mode.
    if (!thread->trapPending && !thread->inSyscall) {
        cpu->squashFromTC(thread->threadId());
    }
}

template <class Impl>
void
O3ThreadContext<Impl>::pcState(const TheISA::PCState &val)
{
    cpu->pcState(val, thread->threadId());

    // Squash if we're not already in a state update mode.
    if (!thread->trapPending && !thread->inSyscall) {
        cpu->squashFromTC(thread->threadId());
    }
}

template <class Impl>
int
O3ThreadContext<Impl>::flattenIntIndex(int reg)
{
    return cpu->isa[thread->threadId()].flattenIntIndex(reg);
}

template <class Impl>
int
O3ThreadContext<Impl>::flattenFloatIndex(int reg)
{
    return cpu->isa[thread->threadId()].flattenFloatIndex(reg);
}

template <class Impl>
void
O3ThreadContext<Impl>::setMiscRegNoEffect(int misc_reg, const MiscReg &val)
{
    cpu->setMiscRegNoEffect(misc_reg, val, thread->threadId());

    // Squash if we're not already in a state update mode.
    if (!thread->trapPending && !thread->inSyscall) {
        cpu->squashFromTC(thread->threadId());
    }
}

template <class Impl>
void
O3ThreadContext<Impl>::setMiscReg(int misc_reg,
                                                const MiscReg &val)
{
    cpu->setMiscReg(misc_reg, val, thread->threadId());

    // Squash if we're not already in a state update mode.
    if (!thread->trapPending && !thread->inSyscall) {
        cpu->squashFromTC(thread->threadId());
    }
}

<|endoftext|>


<commit_before>/*Copyright 2014 George Karagoulis

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 "cryptopp_cryptor.h"
#include "cryptopp_sinks.h"
#include "gutil_d.h"
#include "gutil_exception.h"
#include "gutil_file.h"
#include "gutil_smartpointer.h"
#include "gutil_cryptopp_hash.h"
#include <cryptopp/ccm.h>
#include <cryptopp/aes.h>
#include <cryptopp/osrng.h>
#include <cryptopp/sha3.h>
#include <math.h>
USING_NAMESPACE_GUTIL;

#define DEFAULT_CHUNK_SIZE 1024

namespace
{

// These are allocated at compile time, so consumers
//   of our class don't need to include cryptopp headers
struct d_t
{
    ::CryptoPP::AutoSeededX917RNG< ::CryptoPP::AES> rng;
    ::CryptoPP::CCM< ::CryptoPP::AES, GUtil::CryptoPP::Cryptor::TagLength>::Encryption enc;
    ::CryptoPP::CCM< ::CryptoPP::AES, GUtil::CryptoPP::Cryptor::TagLength>::Decryption dec;
};

}

NAMESPACE_GUTIL1(CryptoPP);

static void __compute_password_hash(byte *result, const char *password,
                                    const byte *salt, GUINT32 salt_len)
{
    ::CryptoPP::SHA3_256 h;
    if(salt)
        h.Update(salt, salt_len);
    h.Update((byte const *)password, password ? strlen(password) : 0);
    h.Final(result);
}

static void __compute_keyfile_hash(byte *result, const char *keyfile,
                                   const byte *salt, GUINT32 salt_len,
                                   IProgressHandler *ph = 0)
{
    SmartPointer<IInput> in;
    if(keyfile != NULL && strlen(keyfile) > 0){
        File *f = new File(keyfile);
        in = f;
        f->Open(File::OpenRead);
    }
    Hash<SHA3_256> h;
    if(salt)
        h.AddData(salt, salt_len);
    h.ComputeHash(result, in, DEFAULT_CHUNK_SIZE, ph);
}

static void __compute_password_hash2(byte *result, const char *password,
                                     const byte *salt, GUINT32 salt_len)
{
    ::CryptoPP::SHA3_512 h;
    if(salt)
        h.Update(salt, salt_len);
    h.Update((byte const *)password, password ? strlen(password) : 0);
    h.Final(result);
}

Cryptor::Cryptor(const char *password, const char *keyfile,
                 const byte *salt, GUINT32 salt_len)
    :m_nonceSize(DefaultNonceSize)
{
    ChangePassword(password, keyfile, salt, salt_len);
    G_D_INIT();
}

Cryptor::Cryptor(const Cryptor &other)
    :m_nonceSize(other.m_nonceSize)
{
    memcpy(m_key, other.m_key, sizeof(m_key));
    memcpy(m_key2, other.m_key2, sizeof(m_key2));
    G_D_INIT();
}

Cryptor::~Cryptor()
{
    G_D_UNINIT();
}

void Cryptor::SetNonceSize(GUINT8 size)
{
    if(size < 7 || 13 < size)
        THROW_NEW_GUTIL_EXCEPTION2(Exception, "Invalid nonce size");
    m_nonceSize = size;
}

void Cryptor::FillRandom(byte *b, GUINT32 l)
{
    G_D;
    d->rng.GenerateBlock(b, l);
}

bool Cryptor::CheckPassword(const char *password, const char *keyfile,
                            const byte *salt, GUINT32 salt_len) const
{
    byte buf_key[sizeof(m_key)] = {};
    byte buf_key2[sizeof(m_key2)] = {};
    if(keyfile == NULL || strlen(keyfile) == 0){
        // Only the password was given (or even a null password)
        __compute_password_hash(buf_key, password, salt, salt_len);
    }
    else{
        // A password and keyfile were given
        __compute_keyfile_hash(buf_key, keyfile, salt, salt_len);
    }
    __compute_password_hash2(buf_key2, password, salt, salt_len);
    return (0 == memcmp(m_key, buf_key, sizeof(buf_key))) &&
           (0 == memcmp(m_key2, buf_key2, sizeof(buf_key2)));
}

void Cryptor::ChangePassword(const char *password, const char *keyfile,
                             const byte *salt, GUINT32 salt_len)
{
    if(keyfile == NULL || strlen(keyfile) == 0){
        // Only the password was given (or even a null password)
        __compute_password_hash(m_key, password, salt, salt_len);
    }
    else{
        // A password and keyfile were given
        __compute_keyfile_hash(m_key, keyfile, salt, salt_len);
    }
    __compute_password_hash2(m_key2, password, salt, salt_len);
}

GUINT64 Cryptor::GetMaxPayloadLength() const
{
    return GetNonceSize() == 7 ? GUINT64_MAX : ((GUINT64)1 << 8*(15-GetNonceSize())) - 1;
}

void Cryptor::SetMaxPayloadLength(GUINT64 len)
{
    SetNonceSize(Min(13, 15 - ((FSB64(len) + 1) >> 3)));
}

void Cryptor::EncryptData(IOutput *out,
                          IInput *pData,
                          IInput *aData,
                          byte const *nonce,
                          GUINT32 chunk_size,
                          IProgressHandler *ph)
{
    G_D;
    SmartArrayPointer<byte> n;
    if(NULL == nonce)
        n.Set(new byte[GetNonceSize()]);
    if(ph) ph->ProgressUpdated(0);

    const GUINT64 aData_len = aData ? aData->BytesAvailable() : 0;
    GUINT64 len = pData ? pData->BytesAvailable() : 0;
    if(len == GUINT32_MAX)
        THROW_NEW_GUTIL_EXCEPTION2(Exception, "Source invalid: Length must be known");

    // With CCM the length is restricted, let's check that here
    if(GetMaxPayloadLength() < len)
        THROW_NEW_GUTIL_EXCEPTION2(Exception, "Payload too large for the given nonce size");

    if(NULL == nonce){
        // Initialize a random IV
        FillRandom(n.Data(), GetNonceSize());
    }
    d->enc.SetKeyWithIV(m_key, sizeof(m_key), nonce == NULL ? n.Data():nonce, GetNonceSize());
    d->enc.SpecifyDataLengths(aData_len + sizeof(m_key2), len);

    GUINT32 read = 0;
    GUINT32 to_read = chunk_size == 0 ? len : chunk_size;
    GUINT32 buf_sz = to_read;
    ::CryptoPP::AuthenticatedEncryptionFilter ef(
                d->enc,
                new OutputInterfaceSink(*out),
                false,
                TagLength);

    // First pass the authenticated data. Our second key goes in here.
    ef.ChannelPut(::CryptoPP::AAD_CHANNEL, m_key2, sizeof(m_key2));

    // If they gave us additional auth data, add it here
    if(aData && 0 < aData->BytesAvailable())
    {
        SmartArrayPointer<byte> a(new byte[aData_len]);
        if(aData_len != aData->ReadBytes(a, aData_len, aData_len))
            THROW_NEW_GUTIL_EXCEPTION2(Exception, "Error reading from auth data source");
        ef.ChannelPut(::CryptoPP::AAD_CHANNEL, a.Data(), aData_len);
    }

    // Then read and pass the plaintext data
    if(read < len)
    {
        SmartArrayPointer<byte> buf(new byte[buf_sz]);
        while(read < len)
        {
            GASSERT(pData);

            if((read + to_read) > len)
                to_read = len - read;

            if(to_read != pData->ReadBytes(buf, buf_sz, to_read))
                THROW_NEW_GUTIL_EXCEPTION2(Exception, "Error reading from source");

            ef.Put(buf, to_read, true);
            if(out)
                out->Flush();
            read += to_read;

            if(ph){
                ph->ProgressUpdated(((float)read / len) * 100);
                if(ph->ShouldOperationCancel())
                    THROW_NEW_GUTIL_EXCEPTION(CancelledOperationException);
            }
        }
    }
    ef.MessageEnd();

    // Write the IV at the very end
    out->WriteBytes(nonce == NULL ? n.Data():nonce, GetNonceSize());
    out->Flush();
    if(ph) ph->ProgressUpdated(100);
}

void Cryptor::DecryptData(IOutput *out,
                          IRandomAccessInput *cData,
                          IInput *aData,
                          GUINT32 chunk_size,
                          IProgressHandler *ph)
{
    G_D;
    GUINT64 len;
    if(ph) ph->ProgressUpdated(0);
    const GUINT64 aData_len = aData ? aData->BytesAvailable() : 0;
    if(cData == NULL || (len = cData->BytesAvailable()) < (GetNonceSize() + TagLength))
        THROW_NEW_GUTIL_EXCEPTION2(Exception, "Invalid data length");
    len = len - (GetNonceSize() + TagLength);

    if(GetMaxPayloadLength() < len)
        THROW_NEW_GUTIL_EXCEPTION2(Exception, "Payload too large for the given nonce size");

    // Read the MAC tag and IV at the end of the crypttext
    SmartArrayPointer<byte> mac_iv(new byte[TagLength + GetNonceSize()]);
    cData->Seek(cData->Length() - (TagLength + GetNonceSize()));
    if(GetNonceSize() + TagLength !=
            cData->ReadBytes(mac_iv.Data(), GetNonceSize() + TagLength, GetNonceSize() + TagLength))
        THROW_NEW_GUTIL_EXCEPTION2(Exception, "Error reading from source");

    // Seek back to the start of the message
    cData->Seek(0);

    // Initialize the decryptor
    d->dec.SetKeyWithIV(m_key, sizeof(m_key), mac_iv.Data() + TagLength, GetNonceSize());
    d->dec.SpecifyDataLengths(aData_len + sizeof(m_key2), len);
    try
    {
        GUINT32 read = 0;
        GUINT32 to_read = chunk_size == 0 ? len : chunk_size;
        GUINT32 buf_sz = to_read;
        ::CryptoPP::AuthenticatedDecryptionFilter df(
                    d->dec,
                    out == NULL ? NULL : new OutputInterfaceSink(*out),
                    ::CryptoPP::AuthenticatedDecryptionFilter::THROW_EXCEPTION |
                    ::CryptoPP::AuthenticatedDecryptionFilter::MAC_AT_BEGIN,
                    TagLength);

        // We have to give it the mac before everything else in order to decrypt both
        //  the pData and the aData
        df.Put(mac_iv.Data(), TagLength);

        // Pass the authenticated data before the plaintext:
        df.ChannelPut(::CryptoPP::AAD_CHANNEL, m_key2, sizeof(m_key2));

        // If they gave us additional auth data, add it here
        if(aData && 0 < aData->BytesAvailable())
        {
            SmartArrayPointer<byte> a(new byte[aData_len]);
            if(aData_len != aData->ReadBytes(a, aData_len, aData_len))
                THROW_NEW_GUTIL_EXCEPTION2(Exception, "Error reading from auth data source");
            df.ChannelPut(::CryptoPP::AAD_CHANNEL, a.Data(), aData_len);
        }

        // Then read and pass the crypttext
        if(read < len)
        {
            SmartArrayPointer<byte> buf(new byte[buf_sz]);
            while(read < len)
            {
                if((read + to_read) > len)
                    to_read = len - read;

                if(to_read != cData->ReadBytes(buf, buf_sz, to_read))
                    THROW_NEW_GUTIL_EXCEPTION2(Exception, "Error reading from source");

                df.Put(buf, to_read, true);
                if(out)
                    out->Flush();
                read += to_read;

                if(ph){
                    ph->ProgressUpdated(((float)read / len) * 100);
                    if(ph->ShouldOperationCancel())
                        THROW_NEW_GUTIL_EXCEPTION(CancelledOperationException);
                }
            }
        }

        // This will throw an exception if authenticity validation failed
        df.MessageEnd();
    }
    catch(const ::CryptoPP::Exception &ex)
    {
        THROW_NEW_GUTIL_EXCEPTION2(AuthenticationException, ex.what());
    }
    if(out) out->Flush();
    if(ph) ph->ProgressUpdated(100);
}

double Cryptor::GetMaxKeyUsageSuggestion(GUINT8 nonce_length)
{
    double ret = 0;
    if(7 <= nonce_length && nonce_length <= 13){
        ret = pow(2, 8*nonce_length - 32);
    }
    return ret;
}

END_NAMESPACE_GUTIL1;
<commit_msg>Fixed a linker error<commit_after>/*Copyright 2014 George Karagoulis

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 "cryptopp_cryptor.h"
#include "cryptopp_sinks.h"
#include "gutil_d.h"
#include "gutil_exception.h"
#include "gutil_file.h"
#include "gutil_smartpointer.h"
#include "gutil_cryptopp_hash.h"
#include <cryptopp/ccm.h>
#include <cryptopp/aes.h>
#include <cryptopp/osrng.h>
#include <cryptopp/sha3.h>
#include <math.h>
USING_NAMESPACE_GUTIL;

#define DEFAULT_CHUNK_SIZE 1024

namespace
{

// These are allocated at compile time, so consumers
//   of our class don't need to include cryptopp headers
struct d_t
{
    ::CryptoPP::AutoSeededX917RNG< ::CryptoPP::AES> rng;
    ::CryptoPP::CCM< ::CryptoPP::AES, GUtil::CryptoPP::Cryptor::TagLength>::Encryption enc;
    ::CryptoPP::CCM< ::CryptoPP::AES, GUtil::CryptoPP::Cryptor::TagLength>::Decryption dec;
};

}

NAMESPACE_GUTIL1(CryptoPP);

// This needs to be defined or we get linker errors since this is used in the code
//  in places other than a const expression
const GUINT32 Cryptor::TagLength;

static void __compute_password_hash(byte *result, const char *password,
                                    const byte *salt, GUINT32 salt_len)
{
    ::CryptoPP::SHA3_256 h;
    if(salt)
        h.Update(salt, salt_len);
    h.Update((byte const *)password, password ? strlen(password) : 0);
    h.Final(result);
}

static void __compute_keyfile_hash(byte *result, const char *keyfile,
                                   const byte *salt, GUINT32 salt_len,
                                   IProgressHandler *ph = 0)
{
    SmartPointer<IInput> in;
    if(keyfile != NULL && strlen(keyfile) > 0){
        File *f = new File(keyfile);
        in = f;
        f->Open(File::OpenRead);
    }
    Hash<SHA3_256> h;
    if(salt)
        h.AddData(salt, salt_len);
    h.ComputeHash(result, in, DEFAULT_CHUNK_SIZE, ph);
}

static void __compute_password_hash2(byte *result, const char *password,
                                     const byte *salt, GUINT32 salt_len)
{
    ::CryptoPP::SHA3_512 h;
    if(salt)
        h.Update(salt, salt_len);
    h.Update((byte const *)password, password ? strlen(password) : 0);
    h.Final(result);
}

Cryptor::Cryptor(const char *password, const char *keyfile,
                 const byte *salt, GUINT32 salt_len)
    :m_nonceSize(DefaultNonceSize)
{
    ChangePassword(password, keyfile, salt, salt_len);
    G_D_INIT();
}

Cryptor::Cryptor(const Cryptor &other)
    :m_nonceSize(other.m_nonceSize)
{
    memcpy(m_key, other.m_key, sizeof(m_key));
    memcpy(m_key2, other.m_key2, sizeof(m_key2));
    G_D_INIT();
}

Cryptor::~Cryptor()
{
    G_D_UNINIT();
}

void Cryptor::SetNonceSize(GUINT8 size)
{
    if(size < 7 || 13 < size)
        THROW_NEW_GUTIL_EXCEPTION2(Exception, "Invalid nonce size");
    m_nonceSize = size;
}

void Cryptor::FillRandom(byte *b, GUINT32 l)
{
    G_D;
    d->rng.GenerateBlock(b, l);
}

bool Cryptor::CheckPassword(const char *password, const char *keyfile,
                            const byte *salt, GUINT32 salt_len) const
{
    byte buf_key[sizeof(m_key)] = {};
    byte buf_key2[sizeof(m_key2)] = {};
    if(keyfile == NULL || strlen(keyfile) == 0){
        // Only the password was given (or even a null password)
        __compute_password_hash(buf_key, password, salt, salt_len);
    }
    else{
        // A password and keyfile were given
        __compute_keyfile_hash(buf_key, keyfile, salt, salt_len);
    }
    __compute_password_hash2(buf_key2, password, salt, salt_len);
    return (0 == memcmp(m_key, buf_key, sizeof(buf_key))) &&
           (0 == memcmp(m_key2, buf_key2, sizeof(buf_key2)));
}

void Cryptor::ChangePassword(const char *password, const char *keyfile,
                             const byte *salt, GUINT32 salt_len)
{
    if(keyfile == NULL || strlen(keyfile) == 0){
        // Only the password was given (or even a null password)
        __compute_password_hash(m_key, password, salt, salt_len);
    }
    else{
        // A password and keyfile were given
        __compute_keyfile_hash(m_key, keyfile, salt, salt_len);
    }
    __compute_password_hash2(m_key2, password, salt, salt_len);
}

GUINT64 Cryptor::GetMaxPayloadLength() const
{
    return GetNonceSize() == 7 ? GUINT64_MAX : ((GUINT64)1 << 8*(15-GetNonceSize())) - 1;
}

void Cryptor::SetMaxPayloadLength(GUINT64 len)
{
    SetNonceSize(Min(13, 15 - ((FSB64(len) + 1) >> 3)));
}

void Cryptor::EncryptData(IOutput *out,
                          IInput *pData,
                          IInput *aData,
                          byte const *nonce,
                          GUINT32 chunk_size,
                          IProgressHandler *ph)
{
    G_D;
    SmartArrayPointer<byte> n;
    if(NULL == nonce)
        n.Set(new byte[GetNonceSize()]);
    if(ph) ph->ProgressUpdated(0);

    const GUINT64 aData_len = aData ? aData->BytesAvailable() : 0;
    GUINT64 len = pData ? pData->BytesAvailable() : 0;
    if(len == GUINT32_MAX)
        THROW_NEW_GUTIL_EXCEPTION2(Exception, "Source invalid: Length must be known");

    // With CCM the length is restricted, let's check that here
    if(GetMaxPayloadLength() < len)
        THROW_NEW_GUTIL_EXCEPTION2(Exception, "Payload too large for the given nonce size");

    if(NULL == nonce){
        // Initialize a random IV
        FillRandom(n.Data(), GetNonceSize());
    }
    d->enc.SetKeyWithIV(m_key, sizeof(m_key), nonce == NULL ? n.Data():nonce, GetNonceSize());
    d->enc.SpecifyDataLengths(aData_len + sizeof(m_key2), len);

    GUINT32 read = 0;
    GUINT32 to_read = chunk_size == 0 ? len : chunk_size;
    GUINT32 buf_sz = to_read;
    ::CryptoPP::AuthenticatedEncryptionFilter ef(
                d->enc,
                new OutputInterfaceSink(*out),
                false,
                TagLength);

    // First pass the authenticated data. Our second key goes in here.
    ef.ChannelPut(::CryptoPP::AAD_CHANNEL, m_key2, sizeof(m_key2));

    // If they gave us additional auth data, add it here
    if(aData && 0 < aData->BytesAvailable())
    {
        SmartArrayPointer<byte> a(new byte[aData_len]);
        if(aData_len != aData->ReadBytes(a, aData_len, aData_len))
            THROW_NEW_GUTIL_EXCEPTION2(Exception, "Error reading from auth data source");
        ef.ChannelPut(::CryptoPP::AAD_CHANNEL, a.Data(), aData_len);
    }

    // Then read and pass the plaintext data
    if(read < len)
    {
        SmartArrayPointer<byte> buf(new byte[buf_sz]);
        while(read < len)
        {
            GASSERT(pData);

            if((read + to_read) > len)
                to_read = len - read;

            if(to_read != pData->ReadBytes(buf, buf_sz, to_read))
                THROW_NEW_GUTIL_EXCEPTION2(Exception, "Error reading from source");

            ef.Put(buf, to_read, true);
            if(out)
                out->Flush();
            read += to_read;

            if(ph){
                ph->ProgressUpdated(((float)read / len) * 100);
                if(ph->ShouldOperationCancel())
                    THROW_NEW_GUTIL_EXCEPTION(CancelledOperationException);
            }
        }
    }
    ef.MessageEnd();

    // Write the IV at the very end
    out->WriteBytes(nonce == NULL ? n.Data():nonce, GetNonceSize());
    out->Flush();
    if(ph) ph->ProgressUpdated(100);
}

void Cryptor::DecryptData(IOutput *out,
                          IRandomAccessInput *cData,
                          IInput *aData,
                          GUINT32 chunk_size,
                          IProgressHandler *ph)
{
    G_D;
    GUINT64 len;
    if(ph) ph->ProgressUpdated(0);
    const GUINT64 aData_len = aData ? aData->BytesAvailable() : 0;
    if(cData == NULL || (len = cData->BytesAvailable()) < (GetNonceSize() + TagLength))
        THROW_NEW_GUTIL_EXCEPTION2(Exception, "Invalid data length");
    len = len - (GetNonceSize() + TagLength);

    if(GetMaxPayloadLength() < len)
        THROW_NEW_GUTIL_EXCEPTION2(Exception, "Payload too large for the given nonce size");

    // Read the MAC tag and IV at the end of the crypttext
    SmartArrayPointer<byte> mac_iv(new byte[TagLength + GetNonceSize()]);
    cData->Seek(cData->Length() - (TagLength + GetNonceSize()));
    if(GetNonceSize() + TagLength !=
            cData->ReadBytes(mac_iv.Data(), GetNonceSize() + TagLength, GetNonceSize() + TagLength))
        THROW_NEW_GUTIL_EXCEPTION2(Exception, "Error reading from source");

    // Seek back to the start of the message
    cData->Seek(0);

    // Initialize the decryptor
    d->dec.SetKeyWithIV(m_key, sizeof(m_key), mac_iv.Data() + TagLength, GetNonceSize());
    d->dec.SpecifyDataLengths(aData_len + sizeof(m_key2), len);
    try
    {
        GUINT32 read = 0;
        GUINT32 to_read = chunk_size == 0 ? len : chunk_size;
        GUINT32 buf_sz = to_read;
        ::CryptoPP::AuthenticatedDecryptionFilter df(
                    d->dec,
                    out == NULL ? NULL : new OutputInterfaceSink(*out),
                    ::CryptoPP::AuthenticatedDecryptionFilter::THROW_EXCEPTION |
                    ::CryptoPP::AuthenticatedDecryptionFilter::MAC_AT_BEGIN,
                    TagLength);

        // We have to give it the mac before everything else in order to decrypt both
        //  the pData and the aData
        df.Put(mac_iv.Data(), TagLength);

        // Pass the authenticated data before the plaintext:
        df.ChannelPut(::CryptoPP::AAD_CHANNEL, m_key2, sizeof(m_key2));

        // If they gave us additional auth data, add it here
        if(aData && 0 < aData->BytesAvailable())
        {
            SmartArrayPointer<byte> a(new byte[aData_len]);
            if(aData_len != aData->ReadBytes(a, aData_len, aData_len))
                THROW_NEW_GUTIL_EXCEPTION2(Exception, "Error reading from auth data source");
            df.ChannelPut(::CryptoPP::AAD_CHANNEL, a.Data(), aData_len);
        }

        // Then read and pass the crypttext
        if(read < len)
        {
            SmartArrayPointer<byte> buf(new byte[buf_sz]);
            while(read < len)
            {
                if((read + to_read) > len)
                    to_read = len - read;

                if(to_read != cData->ReadBytes(buf, buf_sz, to_read))
                    THROW_NEW_GUTIL_EXCEPTION2(Exception, "Error reading from source");

                df.Put(buf, to_read, true);
                if(out)
                    out->Flush();
                read += to_read;

                if(ph){
                    ph->ProgressUpdated(((float)read / len) * 100);
                    if(ph->ShouldOperationCancel())
                        THROW_NEW_GUTIL_EXCEPTION(CancelledOperationException);
                }
            }
        }

        // This will throw an exception if authenticity validation failed
        df.MessageEnd();
    }
    catch(const ::CryptoPP::Exception &ex)
    {
        THROW_NEW_GUTIL_EXCEPTION2(AuthenticationException, ex.what());
    }
    if(out) out->Flush();
    if(ph) ph->ProgressUpdated(100);
}

double Cryptor::GetMaxKeyUsageSuggestion(GUINT8 nonce_length)
{
    double ret = 0;
    if(7 <= nonce_length && nonce_length <= 13){
        ret = pow(2, 8*nonce_length - 32);
    }
    return ret;
}

END_NAMESPACE_GUTIL1;
<|endoftext|>