env_chroot.cc 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. // Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
  2. // This source code is licensed under both the GPLv2 (found in the
  3. // COPYING file in the root directory) and Apache 2.0 License
  4. // (found in the LICENSE.Apache file in the root directory).
  5. #if !defined(OS_WIN)
  6. #include "env/env_chroot.h"
  7. #include <unistd.h> // geteuid
  8. #include <cerrno> // errno
  9. #include <cstdlib> // realpath, free
  10. #include "env/composite_env_wrapper.h"
  11. #include "env/fs_remap.h"
  12. #include "rocksdb/utilities/options_type.h"
  13. #include "util/string_util.h" // errnoStr
  14. namespace ROCKSDB_NAMESPACE {
  15. namespace {
  16. static std::unordered_map<std::string, OptionTypeInfo> chroot_fs_type_info = {
  17. {"chroot_dir", {0, OptionType::kString}}};
  18. } // namespace
  19. ChrootFileSystem::ChrootFileSystem(const std::shared_ptr<FileSystem>& base,
  20. const std::string& chroot_dir)
  21. : RemapFileSystem(base), chroot_dir_(chroot_dir) {
  22. RegisterOptions("chroot_dir", &chroot_dir_, &chroot_fs_type_info);
  23. }
  24. Status ChrootFileSystem::PrepareOptions(const ConfigOptions& options) {
  25. Status s = FileSystemWrapper::PrepareOptions(options);
  26. if (!s.ok()) {
  27. return s;
  28. } else if (chroot_dir_.empty()) {
  29. s = Status::InvalidArgument("ChRootFileSystem requires a chroot dir");
  30. } else {
  31. s = target_->FileExists(chroot_dir_, IOOptions(), nullptr);
  32. }
  33. if (s.ok()) {
  34. #if defined(OS_AIX)
  35. char resolvedName[PATH_MAX];
  36. char* real_chroot_dir = realpath(chroot_dir_.c_str(), resolvedName);
  37. #else
  38. char* real_chroot_dir = realpath(chroot_dir_.c_str(), nullptr);
  39. #endif
  40. // chroot_dir must exist so realpath() returns non-nullptr.
  41. assert(real_chroot_dir != nullptr);
  42. chroot_dir_ = real_chroot_dir;
  43. #if !defined(OS_AIX)
  44. free(real_chroot_dir);
  45. #endif
  46. }
  47. return s;
  48. }
  49. IOStatus ChrootFileSystem::GetTestDirectory(const IOOptions& options,
  50. std::string* path,
  51. IODebugContext* dbg) {
  52. // Adapted from PosixEnv's implementation since it doesn't provide a way to
  53. // create directory in the chroot.
  54. char buf[256];
  55. snprintf(buf, sizeof(buf), "/rocksdbtest-%d", static_cast<int>(geteuid()));
  56. *path = buf;
  57. // Directory may already exist, so ignore return
  58. return CreateDirIfMissing(*path, options, dbg);
  59. }
  60. // Returns status and expanded absolute path including the chroot directory.
  61. // Checks whether the provided path breaks out of the chroot. If it returns
  62. // non-OK status, the returned path should not be used.
  63. std::pair<IOStatus, std::string> ChrootFileSystem::EncodePath(
  64. const std::string& path) {
  65. if (path.empty() || path[0] != '/') {
  66. return {IOStatus::InvalidArgument(path, "Not an absolute path"), ""};
  67. }
  68. std::pair<IOStatus, std::string> res;
  69. res.second = chroot_dir_ + path;
  70. #if defined(OS_AIX)
  71. char resolvedName[PATH_MAX];
  72. char* normalized_path = realpath(res.second.c_str(), resolvedName);
  73. #else
  74. char* normalized_path = realpath(res.second.c_str(), nullptr);
  75. #endif
  76. if (normalized_path == nullptr) {
  77. res.first = IOStatus::NotFound(res.second, errnoStr(errno).c_str());
  78. } else if (strlen(normalized_path) < chroot_dir_.size() ||
  79. strncmp(normalized_path, chroot_dir_.c_str(),
  80. chroot_dir_.size()) != 0) {
  81. res.first = IOStatus::IOError(res.second,
  82. "Attempted to access path outside chroot");
  83. } else {
  84. res.first = IOStatus::OK();
  85. }
  86. #if !defined(OS_AIX)
  87. free(normalized_path);
  88. #endif
  89. return res;
  90. }
  91. // Similar to EncodePath() except assumes the basename in the path hasn't been
  92. // created yet.
  93. std::pair<IOStatus, std::string> ChrootFileSystem::EncodePathWithNewBasename(
  94. const std::string& path) {
  95. if (path.empty() || path[0] != '/') {
  96. return {IOStatus::InvalidArgument(path, "Not an absolute path"), ""};
  97. }
  98. // Basename may be followed by trailing slashes
  99. size_t final_idx = path.find_last_not_of('/');
  100. if (final_idx == std::string::npos) {
  101. // It's only slashes so no basename to extract
  102. return EncodePath(path);
  103. }
  104. // Pull off the basename temporarily since realname(3) (used by
  105. // EncodePath()) requires a path that exists
  106. size_t base_sep = path.rfind('/', final_idx);
  107. auto status_and_enc_path = EncodePath(path.substr(0, base_sep + 1));
  108. status_and_enc_path.second.append(path.substr(base_sep + 1));
  109. return status_and_enc_path;
  110. }
  111. std::shared_ptr<FileSystem> NewChrootFileSystem(
  112. const std::shared_ptr<FileSystem>& base, const std::string& chroot_dir) {
  113. auto chroot_fs = std::make_shared<ChrootFileSystem>(base, chroot_dir);
  114. Status s = chroot_fs->PrepareOptions(ConfigOptions());
  115. if (s.ok()) {
  116. return chroot_fs;
  117. } else {
  118. return nullptr;
  119. }
  120. }
  121. Env* NewChrootEnv(Env* base_env, const std::string& chroot_dir) {
  122. if (!base_env->FileExists(chroot_dir).ok()) {
  123. return nullptr;
  124. }
  125. auto chroot_fs = NewChrootFileSystem(base_env->GetFileSystem(), chroot_dir);
  126. if (chroot_fs != nullptr) {
  127. return new CompositeEnvWrapper(base_env, chroot_fs);
  128. } else {
  129. return nullptr;
  130. }
  131. }
  132. } // namespace ROCKSDB_NAMESPACE
  133. #endif // !defined(OS_WIN)