<commit_before>/*
 *  Copyright (c) 2014, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

#include <exception>
#include <sstream>

#include <fcntl.h>
#include <sys/stat.h>

#include <boost/algorithm/string/join.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>

#include <osquery/core.h>
#include <osquery/filesystem.h>
#include <osquery/logger.h>
#include <osquery/sql.h>

namespace pt = boost::property_tree;
namespace fs = boost::filesystem;

namespace osquery {

Status writeTextFile(const boost::filesystem::path& path,
                     const std::string& content,
                     int permissions,
                     bool force_permissions) {
  // Open the file with the request permissions.
  int output_fd =
      open(path.c_str(), O_CREAT | O_APPEND | O_WRONLY, permissions);
  if (output_fd <= 0) {
    return Status(1, "Could not create file: " + path.string());
  }

  // If the file existed with different permissions before our open
  // they must be restricted.
  if (chmod(path.c_str(), permissions) != 0) {
    // Could not change the file to the requested permissions.
    return Status(1, "Failed to change permissions for file: " + path.string());
  }

  auto bytes = write(output_fd, content.c_str(), content.size());
  if (bytes != content.size()) {
    close(output_fd);
    return Status(1, "Failed to write contents to file: " + path.string());
  }

  close(output_fd);
  return Status(0, "OK");
}

Status readFile(const boost::filesystem::path& path, std::string& content) {
  auto path_exists = pathExists(path);
  if (!path_exists.ok()) {
    return path_exists;
  }

  std::stringstream buffer;
  fs::ifstream file_h(path);
  if (file_h.is_open()) {
    buffer << file_h.rdbuf();
    if (file_h.bad()) {
      return Status(1, "Error reading file: " + path.string());
    }
    content.assign(std::move(buffer.str()));
  } else {
    return Status(1, "Could not open file: " + path.string());
  }

  return Status(0, "OK");
}

Status isWritable(const boost::filesystem::path& path) {
  auto path_exists = pathExists(path);
  if (!path_exists.ok()) {
    return path_exists;
  }

  if (access(path.c_str(), W_OK) == 0) {
    return Status(0, "OK");
  }
  return Status(1, "Path is not writable: " + path.string());
}

Status isReadable(const boost::filesystem::path& path) {
  auto path_exists = pathExists(path);
  if (!path_exists.ok()) {
    return path_exists;
  }

  if (access(path.c_str(), R_OK) == 0) {
    return Status(0, "OK");
  }
  return Status(1, "Path is not readable: " + path.string());
}

Status pathExists(const boost::filesystem::path& path) {
  if (path.empty()) {
    return Status(1, "-1");
  }

  // A tri-state determination of presence
  try {
    if (!boost::filesystem::exists(path)) {
      return Status(1, "0");
    }
  } catch (boost::filesystem::filesystem_error e) {
    return Status(1, e.what());
  }
  return Status(0, "1");
}

Status remove(const boost::filesystem::path& path) {
  auto status_code = std::remove(path.string().c_str());
  return Status(status_code, "N/A");
}

Status listFilesInDirectory(const boost::filesystem::path& path,
                            std::vector<std::string>& results,
                            bool ignore_error) {
  try {
    if (!boost::filesystem::exists(path)) {
      return Status(1, "Directory not found: " + path.string());
    }

    if (!boost::filesystem::is_directory(path)) {
      return Status(1, "Supplied path is not a directory: " + path.string());
    }
  } catch (const boost::filesystem::filesystem_error& e) {
    return Status(1, e.what());
    }

    boost::filesystem::directory_iterator begin_iter(path);
    boost::filesystem::directory_iterator end_iter;
    for (; begin_iter != end_iter; begin_iter++) {
      try {
        if (boost::filesystem::is_regular_file(begin_iter->path())) {
          results.push_back(begin_iter->path().string());
        }
      } catch (const boost::filesystem::filesystem_error& e) {
        if (ignore_error == 0) {
          return Status(1, e.what());
        }
      }
    }

    return Status(0, "OK");
}

Status listDirectoriesInDirectory(const boost::filesystem::path& path,
                                  std::vector<std::string>& results,
                                  bool ignore_error) {
  try {
    if (!boost::filesystem::exists(path)) {
      return Status(1, "Directory not found");
    }

    auto stat = pathExists(path);
    if (!stat.ok()) {
      return stat;
    }

    stat = isDirectory(path);
    if (!stat.ok()) {
      return stat;
    }
  } catch (const boost::filesystem::filesystem_error& e) {
    return Status(1, e.what());
    }

    boost::filesystem::directory_iterator begin_iter(path);
    boost::filesystem::directory_iterator end_iter;
    for (; begin_iter != end_iter; begin_iter++) {
      try {
        if (boost::filesystem::is_directory(begin_iter->path())) {
          results.push_back(begin_iter->path().string());
        }
      } catch (const boost::filesystem::filesystem_error& e) {
        if (ignore_error == 0) {
          return Status(1, e.what());
        }
      }
    }

    return Status(0, "OK");
}

/**
 * @brief Drill down recursively and list all sub files
 *
 * This functions purpose is to take a path with no wildcards
 * and it will recursively go through all files and and return
 * them in the results vector.
 *
 * @param fs_path The entire resolved path
 * @param results The vector where results will be returned
 * @param rec_depth How many recursions deep the current execution is at
 *
 * @return An instance of osquery::Status indicating the success of failure of
 * the operation
 */
Status doubleStarTraversal(const boost::filesystem::path& fs_path,
                           std::vector<std::string>& results,
                           ReturnSetting setting,
                           unsigned int rec_depth) {
  if (rec_depth >= kMaxDirectoryTraversalDepth) {
    return Status(2, fs_path.string().c_str());
  }
  // List files first
  if (setting & REC_LIST_FILES) {<commit_msg>Fixing hopefully the last filesystem exception<commit_after>/*
 *  Copyright (c) 2014, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

#include <exception>
#include <sstream>

#include <fcntl.h>
#include <sys/stat.h>

#include <boost/algorithm/string/join.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>

#include <osquery/core.h>
#include <osquery/filesystem.h>
#include <osquery/logger.h>
#include <osquery/sql.h>

namespace pt = boost::property_tree;
namespace fs = boost::filesystem;

namespace osquery {

Status writeTextFile(const boost::filesystem::path& path,
                     const std::string& content,
                     int permissions,
                     bool force_permissions) {
  // Open the file with the request permissions.
  int output_fd =
      open(path.c_str(), O_CREAT | O_APPEND | O_WRONLY, permissions);
  if (output_fd <= 0) {
    return Status(1, "Could not create file: " + path.string());
  }

  // If the file existed with different permissions before our open
  // they must be restricted.
  if (chmod(path.c_str(), permissions) != 0) {
    // Could not change the file to the requested permissions.
    return Status(1, "Failed to change permissions for file: " + path.string());
  }

  auto bytes = write(output_fd, content.c_str(), content.size());
  if (bytes != content.size()) {
    close(output_fd);
    return Status(1, "Failed to write contents to file: " + path.string());
  }

  close(output_fd);
  return Status(0, "OK");
}

Status readFile(const boost::filesystem::path& path, std::string& content) {
  auto path_exists = pathExists(path);
  if (!path_exists.ok()) {
    return path_exists;
  }

  std::stringstream buffer;
  fs::ifstream file_h(path);
  if (file_h.is_open()) {
    buffer << file_h.rdbuf();
    if (file_h.bad()) {
      return Status(1, "Error reading file: " + path.string());
    }
    content.assign(std::move(buffer.str()));
  } else {
    return Status(1, "Could not open file: " + path.string());
  }

  return Status(0, "OK");
}

Status isWritable(const boost::filesystem::path& path) {
  auto path_exists = pathExists(path);
  if (!path_exists.ok()) {
    return path_exists;
  }

  if (access(path.c_str(), W_OK) == 0) {
    return Status(0, "OK");
  }
  return Status(1, "Path is not writable: " + path.string());
}

Status isReadable(const boost::filesystem::path& path) {
  auto path_exists = pathExists(path);
  if (!path_exists.ok()) {
    return path_exists;
  }

  if (access(path.c_str(), R_OK) == 0) {
    return Status(0, "OK");
  }
  return Status(1, "Path is not readable: " + path.string());
}

Status pathExists(const boost::filesystem::path& path) {
  if (path.empty()) {
    return Status(1, "-1");
  }

  // A tri-state determination of presence
  try {
    if (!boost::filesystem::exists(path)) {
      return Status(1, "0");
    }
  } catch (boost::filesystem::filesystem_error e) {
    return Status(1, e.what());
  }
  return Status(0, "1");
}

Status remove(const boost::filesystem::path& path) {
  auto status_code = std::remove(path.string().c_str());
  return Status(status_code, "N/A");
}

Status listFilesInDirectory(const boost::filesystem::path& path,
                            std::vector<std::string>& results,
                            bool ignore_error) {
  boost::filesystem::directory_iterator begin_iter;

  try {
    if (!boost::filesystem::exists(path)) {
      return Status(1, "Directory not found: " + path.string());
    }

    if (!boost::filesystem::is_directory(path)) {
      return Status(1, "Supplied path is not a directory: " + path.string());
    }
    begin_iter = boost::filesystem::directory_iterator(path);

  } catch (const boost::filesystem::filesystem_error& e) {
    return Status(1, e.what());
  }

  boost::filesystem::directory_iterator end_iter;
  for (; begin_iter != end_iter; begin_iter++) {
    try {
      if (boost::filesystem::is_regular_file(begin_iter->path())) {
        results.push_back(begin_iter->path().string());
      }
    } catch (const boost::filesystem::filesystem_error& e) {
      if (ignore_error == 0) {
        return Status(1, e.what());
      }
    }
  }
  return Status(0, "OK");
}

Status listDirectoriesInDirectory(const boost::filesystem::path& path,
                                  std::vector<std::string>& results,
                                  bool ignore_error) {
  boost::filesystem::directory_iterator begin_iter;
  try {
    if (!boost::filesystem::exists(path)) {
      return Status(1, "Directory not found");
    }

    auto stat = pathExists(path);
    if (!stat.ok()) {
      return stat;
    }

    stat = isDirectory(path);
    if (!stat.ok()) {
      return stat;
    }
    begin_iter = boost::filesystem::directory_iterator(path);
  } catch (const boost::filesystem::filesystem_error& e) {
    return Status(1, e.what());
  }

  boost::filesystem::directory_iterator end_iter;
  for (; begin_iter != end_iter; begin_iter++) {
    try {
      if (boost::filesystem::is_directory(begin_iter->path())) {
        results.push_back(begin_iter->path().string());
      }
    } catch (const boost::filesystem::filesystem_error& e) {
      if (ignore_error == 0) {
        return Status(1, e.what());
      }
    }
  }
  return Status(0, "OK");
}

/**
 * @brief Drill down recursively and list all sub files
 *
 * This functions purpose is to take a path with no wildcards
 * and it will recursively go through all files and and return
 * them in the results vector.
 *
 * @param fs_path The entire resolved path
 * @param results The vector where results will be returned
 * @param rec_depth How many recursions deep the current execution is at
 *
 * @return An instance of osquery::Status indicating the success of failure of
 * the operation
 */
Status doubleStarTraversal(const boost::filesystem::path& fs_path,
                           std::vector<std::string>& results,
                           ReturnSetting setting,
                           unsigned int rec_depth) {
  if (rec_depth >= kMaxDirectoryTraversalDepth) {
    return Status(2, fs_path.string().c_str());
  }
  // List files first
  if (setting & REC_LIST_FILES) {
{'fraction_non_alphanumeric': 0.13868556899607057, 'fraction_numerical': 0.005855612913167424, 'mean_word_length': 3.1443167305236273, 'pattern_counts': {'":': 0, '<': 43, '<?xml version=': 0, '>': 47, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4533386', 'n_tokens_mistral': 4066, 'n_tokens_neox': 3988, 'n_words': 1061}