hdfs-chown.cc 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. /**
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. #include <future>
  19. #include <iostream>
  20. #include <memory>
  21. #include <mutex>
  22. #include <ostream>
  23. #include <sstream>
  24. #include <string>
  25. #include <vector>
  26. #include "hdfs-chown.h"
  27. #include "tools_common.h"
  28. namespace hdfs::tools {
  29. Chown::Chown(const int argc, char **argv) : HdfsTool(argc, argv) {}
  30. bool Chown::Initialize() {
  31. auto add_options = opt_desc_.add_options();
  32. add_options(
  33. "help,h",
  34. "Change the owner and/or group of each FILE to OWNER and/or GROUP.");
  35. add_options("file", po::value<std::string>(),
  36. "The path to the file whose ownership needs to be modified");
  37. add_options("recursive,R", "Operate on files and directories recursively");
  38. add_options(
  39. "user-group", po::value<std::string>(),
  40. "The user:group to which the file's ownership needs to be changed to");
  41. // An exception is thrown if these arguments are missing or if the arguments'
  42. // count doesn't tally.
  43. pos_opt_desc_.add("user-group", 1);
  44. pos_opt_desc_.add("file", 1);
  45. po::store(po::command_line_parser(argc_, argv_)
  46. .options(opt_desc_)
  47. .positional(pos_opt_desc_)
  48. .run(),
  49. opt_val_);
  50. po::notify(opt_val_);
  51. return true;
  52. }
  53. bool Chown::ValidateConstraints() const {
  54. // Only "help" is allowed as single argument
  55. if (argc_ == 2) {
  56. return opt_val_.count("help");
  57. }
  58. // Rest of the cases must contain more than 2 arguments on the command line
  59. return argc_ > 2;
  60. }
  61. std::string Chown ::GetDescription() const {
  62. std::stringstream desc;
  63. desc << "Usage: hdfs_chown [OPTION] [OWNER][:[GROUP]] FILE" << std::endl
  64. << std::endl
  65. << "Change the owner and/or group of each FILE to OWNER and/or GROUP."
  66. << std::endl
  67. << "The user must be a super-user. Additional information is in the "
  68. "Permissions Guide:"
  69. << std::endl
  70. << "https://hadoop.apache.org/docs/r2.7.1/hadoop-project-dist/"
  71. "hadoop-hdfs/HdfsPermissionsGuide.html"
  72. << std::endl
  73. << std::endl
  74. << " -R operate on files and directories recursively" << std::endl
  75. << " -h display this help and exit" << std::endl
  76. << std::endl
  77. << "Owner is unchanged if missing. Group is unchanged if missing."
  78. << std::endl
  79. << "OWNER and GROUP may be numeric as well as symbolic." << std::endl
  80. << std::endl
  81. << "Examples:" << std::endl
  82. << "hdfs_chown -R new_owner:new_group "
  83. "hdfs://localhost.localdomain:8020/dir/file"
  84. << std::endl
  85. << "hdfs_chown new_owner /dir/file" << std::endl;
  86. return desc.str();
  87. }
  88. bool Chown::Do() {
  89. if (!Initialize()) {
  90. std::cerr << "Unable to initialize HDFS chown tool" << std::endl;
  91. return false;
  92. }
  93. if (!ValidateConstraints()) {
  94. std::cout << GetDescription();
  95. return false;
  96. }
  97. if (opt_val_.count("help") > 0) {
  98. return HandleHelp();
  99. }
  100. if (opt_val_.count("file") > 0 && opt_val_.count("user-group") > 0) {
  101. const auto file = opt_val_["file"].as<std::string>();
  102. const auto recursive = opt_val_.count("recursive") > 0;
  103. const Ownership ownership(opt_val_["user-group"].as<std::string>());
  104. return HandlePath(ownership, recursive, file);
  105. }
  106. return true;
  107. }
  108. bool Chown::HandleHelp() const {
  109. std::cout << GetDescription();
  110. return true;
  111. }
  112. bool Chown::HandlePath(const Ownership &ownership, const bool recursive,
  113. const std::string &file) const {
  114. // Building a URI object from the given uri_path
  115. auto uri = hdfs::parse_path_or_exit(file);
  116. const auto fs = hdfs::doConnect(uri, true);
  117. if (!fs) {
  118. std::cerr << "Could not connect the file system. " << std::endl;
  119. return false;
  120. }
  121. // Wrap async FileSystem::SetOwner with promise to make it a blocking call
  122. auto promise = std::make_shared<std::promise<hdfs::Status>>();
  123. auto future(promise->get_future());
  124. auto handler = [promise](const hdfs::Status &s) { promise->set_value(s); };
  125. if (!recursive) {
  126. fs->SetOwner(uri.get_path(), ownership.GetUser(),
  127. ownership.GetGroup().value_or(""), handler);
  128. } else {
  129. /*
  130. * Allocating shared state, which includes: username and groupname to be
  131. * set, handler to be called, request counter, and a boolean to keep track
  132. * if find is done
  133. */
  134. auto state = std::make_shared<OwnerState>(ownership.GetUser(),
  135. ownership.GetGroup().value_or(""),
  136. handler, 0, false);
  137. /*
  138. * Keep requesting more from Find until we process the entire listing. Call
  139. * handler when Find is done and request counter is 0. Find guarantees that
  140. * the handler will only be called once at a time so we do not need locking
  141. * in handler_find.
  142. */
  143. auto handler_find = [fs,
  144. state](const hdfs::Status &status_find,
  145. const std::vector<hdfs::StatInfo> &stat_infos,
  146. const bool has_more_results) -> bool {
  147. /*
  148. * For each result returned by Find we call async SetOwner with the
  149. * handler below. SetOwner DOES NOT guarantee that the handler will only
  150. * be called once at a time, so we DO need locking in handler_set_owner.
  151. */
  152. auto handler_set_owner = [state](const hdfs::Status &status_set_owner) {
  153. std::lock_guard guard(state->lock);
  154. // Decrement the counter once since we are done with this async call
  155. if (!status_set_owner.ok() && state->status.ok()) {
  156. // We make sure we set state->status only on the first error.
  157. state->status = status_set_owner;
  158. }
  159. state->request_counter--;
  160. if (state->request_counter == 0 && state->find_is_done) {
  161. state->handler(state->status); // exit
  162. }
  163. };
  164. if (!stat_infos.empty() && state->status.ok()) {
  165. for (const auto &s : stat_infos) {
  166. // Launch an asynchronous call to SetOwner for every returned result
  167. state->request_counter++;
  168. fs->SetOwner(s.full_path, state->user, state->group,
  169. handler_set_owner);
  170. }
  171. }
  172. /*
  173. * Lock this section because handler_set_owner might be accessing the same
  174. * shared variables simultaneously.
  175. */
  176. std::lock_guard guard(state->lock);
  177. if (!status_find.ok() && state->status.ok()) {
  178. // We make sure we set state->status only on the first error.
  179. state->status = status_find;
  180. }
  181. if (!has_more_results) {
  182. state->find_is_done = true;
  183. if (state->request_counter == 0) {
  184. state->handler(state->status); // exit
  185. }
  186. return false;
  187. }
  188. return true;
  189. };
  190. // Asynchronous call to Find
  191. fs->Find(uri.get_path(), "*", hdfs::FileSystem::GetDefaultFindMaxDepth(),
  192. handler_find);
  193. }
  194. // Block until promise is set
  195. const auto status = future.get();
  196. if (!status.ok()) {
  197. std::cerr << "Error: " << status.ToString() << std::endl;
  198. return false;
  199. }
  200. return true;
  201. }
  202. } // namespace hdfs::tools