/Users/deen/code/yugabyte-db/src/yb/util/env_util.cc
| Line | Count | Source (jump to first uncovered line) | 
| 1 |  | // Licensed to the Apache Software Foundation (ASF) under one | 
| 2 |  | // or more contributor license agreements.  See the NOTICE file | 
| 3 |  | // distributed with this work for additional information | 
| 4 |  | // regarding copyright ownership.  The ASF licenses this file | 
| 5 |  | // to you under the Apache License, Version 2.0 (the | 
| 6 |  | // "License"); you may not use this file except in compliance | 
| 7 |  | // with the License.  You may obtain a copy of the License at | 
| 8 |  | // | 
| 9 |  | //   http://www.apache.org/licenses/LICENSE-2.0 | 
| 10 |  | // | 
| 11 |  | // Unless required by applicable law or agreed to in writing, | 
| 12 |  | // software distributed under the License is distributed on an | 
| 13 |  | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | 
| 14 |  | // KIND, either express or implied.  See the License for the | 
| 15 |  | // specific language governing permissions and limitations | 
| 16 |  | // under the License. | 
| 17 |  | // | 
| 18 |  | // The following only applies to changes made to this file as part of YugaByte development. | 
| 19 |  | // | 
| 20 |  | // Portions Copyright (c) YugaByte, Inc. | 
| 21 |  | // | 
| 22 |  | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except | 
| 23 |  | // in compliance with the License.  You may obtain a copy of the License at | 
| 24 |  | // | 
| 25 |  | // http://www.apache.org/licenses/LICENSE-2.0 | 
| 26 |  | // | 
| 27 |  | // Unless required by applicable law or agreed to in writing, software distributed under the License | 
| 28 |  | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express | 
| 29 |  | // or implied.  See the License for the specific language governing permissions and limitations | 
| 30 |  | // under the License. | 
| 31 |  | // | 
| 32 |  | #include "yb/util/env_util.h" | 
| 33 |  |  | 
| 34 |  | #include <memory> | 
| 35 |  | #include <string> | 
| 36 |  |  | 
| 37 |  | #include <boost/container/small_vector.hpp> | 
| 38 |  |  | 
| 39 |  | #include "yb/gutil/strings/util.h" | 
| 40 |  | #include "yb/util/env.h" | 
| 41 |  | #include "yb/util/path_util.h" | 
| 42 |  | #include "yb/util/result.h" | 
| 43 |  | #include "yb/util/status_log.h" | 
| 44 |  |  | 
| 45 |  | using strings::Substitute; | 
| 46 |  | using std::shared_ptr; | 
| 47 |  | using std::string; | 
| 48 |  |  | 
| 49 |  | namespace yb { | 
| 50 |  | namespace env_util { | 
| 51 |  |  | 
| 52 |  | // We use this suffix in the "external build directory" mode, i.e. the source could be in | 
| 53 |  | // ~/code/yugabyte the build directories for different configurations will all be in | 
| 54 |  | // ~/code/yugabyte__build. This will prevent IDEs from trying to parse build artifacts. | 
| 55 |  | const string kExternalBuildDirSuffix = "__build"; | 
| 56 |  |  | 
| 57 | 21.7k | std::string GetRootDir(const string& search_for_dir) { | 
| 58 | 21.7k |   char* yb_home = getenv("YB_HOME"); | 
| 59 | 21.7k |   if (yb_home) { | 
| 60 | 0 |     return yb_home; | 
| 61 | 0 |   } | 
| 62 |  |  | 
| 63 |  |   // If YB_HOME is not set, we use the path where the binary is located | 
| 64 |  |   // (e.g., /opt/yugabyte/tserver/bin/yb-tserver) to determine the doc root. | 
| 65 |  |   // To find "www"'s location, we search whether "www" exists at each directory, starting with | 
| 66 |  |   // the directory where the current binary (yb-tserver, or yb-master) is located. | 
| 67 |  |   // During each iteration, we keep going up one directory and do the search again. | 
| 68 |  |   // If we can't find a directory that contains "www", we return a default value for now. | 
| 69 | 21.7k |   string executable_path; | 
| 70 | 21.7k |   auto status = Env::Default()->GetExecutablePath(&executable_path); | 
| 71 | 21.7k |   if (!status.ok()) { | 
| 72 | 0 |     LOG(WARNING) << "Ignoring status error: " << status.ToString(); | 
| 73 | 0 |     return ""; | 
| 74 | 0 |   } | 
| 75 |  |  | 
| 76 | 21.7k |   auto path = executable_path; | 
| 77 | 56.3k |   while (path != "/") { | 
| 78 | 56.3k |     path = DirName(path); | 
| 79 |  |  | 
| 80 | 56.3k |     boost::container::small_vector<string, 2> candidates { path }; | 
| 81 | 56.3k |     if (HasSuffixString(path, kExternalBuildDirSuffix)) { | 
| 82 | 0 |       candidates.push_back(std::string(path.begin(), path.end() - kExternalBuildDirSuffix.size())); | 
| 83 | 0 |       if (candidates.back().back() == '/') { | 
| 84 |  |         // path was of the ".../__build" form instead of ".../<some_name>__build", ignore it. | 
| 85 | 0 |         candidates.pop_back(); | 
| 86 | 0 |       } | 
| 87 | 0 |     } | 
| 88 | 56.3k |     for (const auto& candidate_path : candidates) { | 
| 89 | 56.3k |       auto sub_dir = JoinPathSegments(candidate_path, search_for_dir); | 
| 90 | 56.3k |       bool is_dir = false; | 
| 91 | 56.3k |       auto status = Env::Default()->IsDirectory(sub_dir, &is_dir); | 
| 92 | 56.3k |       if (!status.ok()) { | 
| 93 | 34.5k |         continue; | 
| 94 | 34.5k |       } | 
| 95 | 21.7k |       if (is_dir) { | 
| 96 | 21.7k |         return candidate_path; | 
| 97 | 21.7k |       } | 
| 98 | 21.7k |     } | 
| 99 | 56.3k |   } | 
| 100 |  |  | 
| 101 | 0 |   LOG(ERROR) << "Unable to find '" << search_for_dir | 
| 102 | 0 |              << "' directory by starting the search at path " << DirName(executable_path) | 
| 103 | 0 |              << " and walking up the directory structure"; | 
| 104 |  |  | 
| 105 |  |   // Return a path. | 
| 106 | 0 |   return DirName(DirName(executable_path)); | 
| 107 | 21.7k | } | 
| 108 |  |  | 
| 109 |  | Status OpenFileForWrite(Env* env, const string& path, | 
| 110 | 9 |                         shared_ptr<WritableFile>* file) { | 
| 111 | 9 |   return OpenFileForWrite(WritableFileOptions(), env, path, file); | 
| 112 | 9 | } | 
| 113 |  |  | 
| 114 |  | Status OpenFileForWrite(const WritableFileOptions& opts, | 
| 115 |  |                         Env *env, const string &path, | 
| 116 | 17 |                         shared_ptr<WritableFile> *file) { | 
| 117 | 17 |   std::unique_ptr<WritableFile> w; | 
| 118 | 17 |   RETURN_NOT_OK(env->NewWritableFile(opts, path, &w)); | 
| 119 | 15 |   file->reset(w.release()); | 
| 120 | 15 |   return Status::OK(); | 
| 121 | 17 | } | 
| 122 |  |  | 
| 123 |  | Status OpenFileForRandom(Env *env, const string &path, | 
| 124 | 226k |                          shared_ptr<RandomAccessFile> *file) { | 
| 125 | 226k |   std::unique_ptr<RandomAccessFile> r; | 
| 126 | 226k |   RETURN_NOT_OK(env->NewRandomAccessFile(path, &r)); | 
| 127 | 226k |   file->reset(r.release()); | 
| 128 | 226k |   return Status::OK(); | 
| 129 | 226k | } | 
| 130 |  |  | 
| 131 |  | Status OpenFileForSequential(Env *env, const string &path, | 
| 132 | 1.80k |                              shared_ptr<SequentialFile> *file) { | 
| 133 | 1.80k |   std::unique_ptr<SequentialFile> r; | 
| 134 | 1.80k |   RETURN_NOT_OK(env->NewSequentialFile(path, &r)); | 
| 135 | 1.80k |   file->reset(r.release()); | 
| 136 | 1.80k |   return Status::OK(); | 
| 137 | 1.80k | } | 
| 138 |  |  | 
| 139 |  | Status ReadFully(RandomAccessFile* file, uint64_t offset, size_t n, | 
| 140 | 2.41M |                  Slice* result, uint8_t* scratch) { | 
| 141 |  |  | 
| 142 | 2.41M |   bool first_read = true; | 
| 143 |  |  | 
| 144 | 2.41M |   size_t rem = n; | 
| 145 | 2.41M |   uint8_t* dst = scratch; | 
| 146 | 2.41M |   while (rem > 0) { | 
| 147 | 2.41M |     Slice this_result; | 
| 148 | 2.41M |     RETURN_NOT_OK(file->Read(offset, rem, &this_result, dst)); | 
| 149 | 2.41M |     DCHECK_LE(this_result.size(), rem); | 
| 150 | 2.41M |     if (this_result.size() == 0) { | 
| 151 |  |       // EOF | 
| 152 | 1 |       return STATUS(IOError, Substitute("EOF trying to read $0 bytes at offset $1", | 
| 153 | 1 |                                         n, offset)); | 
| 154 | 1 |     } | 
| 155 |  |  | 
| 156 | 2.41M |     if (first_read && this_result.size() == n) { | 
| 157 |  |       // If it's the first read, we can return a zero-copy array. | 
| 158 | 2.41M |       *result = this_result; | 
| 159 | 2.41M |       return Status::OK(); | 
| 160 | 2.41M |     } | 
| 161 | 18.4E |     first_read = false; | 
| 162 |  |  | 
| 163 |  |     // Otherwise, we're going to have to do more reads and stitch | 
| 164 |  |     // each read together. | 
| 165 | 18.4E |     this_result.relocate(dst); | 
| 166 | 18.4E |     dst += this_result.size(); | 
| 167 | 18.4E |     rem -= this_result.size(); | 
| 168 | 18.4E |     offset += this_result.size(); | 
| 169 | 18.4E |   } | 
| 170 | 18.4E |   DCHECK_EQ(0, rem); | 
| 171 | 18.4E |   *result = Slice(scratch, n); | 
| 172 | 18.4E |   return Status::OK(); | 
| 173 | 2.41M | } | 
| 174 |  |  | 
| 175 | 1.65M | Status CreateDirIfMissing(Env* env, const string& path, bool* created) { | 
| 176 | 1.65M |   Status s = env->CreateDir(path); | 
| 177 | 1.65M |   if (created != nullptr) { | 
| 178 | 409k |     *created = s.ok(); | 
| 179 | 409k |   } | 
| 180 | 1.01M |   return s.IsAlreadyPresent() ? Status::OK() : s; | 
| 181 | 1.65M | } | 
| 182 |  |  | 
| 183 |  | Status CopyFile(Env* env, const string& source_path, const string& dest_path, | 
| 184 | 118 |                 WritableFileOptions opts) { | 
| 185 | 118 |   std::unique_ptr<SequentialFile> source; | 
| 186 | 118 |   RETURN_NOT_OK(env->NewSequentialFile(source_path, &source)); | 
| 187 | 118 |   uint64_t size = VERIFY_RESULT(env->GetFileSize(source_path)); | 
| 188 |  |  | 
| 189 | 118 |   std::unique_ptr<WritableFile> dest; | 
| 190 | 118 |   RETURN_NOT_OK(env->NewWritableFile(opts, dest_path, &dest)); | 
| 191 | 118 |   RETURN_NOT_OK(dest->PreAllocate(size)); | 
| 192 |  |  | 
| 193 | 118 |   const int32_t kBufferSize = 1024 * 1024; | 
| 194 | 118 |   std::unique_ptr<uint8_t[]> scratch(new uint8_t[kBufferSize]); | 
| 195 |  |  | 
| 196 | 118 |   uint64_t bytes_read = 0; | 
| 197 | 237 |   while (bytes_read < size) { | 
| 198 | 119 |     uint64_t max_bytes_to_read = std::min<uint64_t>(size - bytes_read, kBufferSize); | 
| 199 | 119 |     Slice data; | 
| 200 | 119 |     RETURN_NOT_OK(source->Read(max_bytes_to_read, &data, scratch.get())); | 
| 201 | 119 |     RETURN_NOT_OK(dest->Append(data)); | 
| 202 | 119 |     bytes_read += data.size(); | 
| 203 | 119 |   } | 
| 204 | 118 |   return Status::OK(); | 
| 205 | 118 | } | 
| 206 |  |  | 
| 207 |  | ScopedFileDeleter::ScopedFileDeleter(Env* env, std::string path) | 
| 208 | 1.21M |     : env_(DCHECK_NOTNULL(env)), path_(std::move(path)), should_delete_(true) {} | 
| 209 |  |  | 
| 210 | 1.21M | ScopedFileDeleter::~ScopedFileDeleter() { | 
| 211 | 1.21M |   if (should_delete_) { | 
| 212 | 0 |     bool is_dir; | 
| 213 | 0 |     Status s = env_->IsDirectory(path_, &is_dir); | 
| 214 | 0 |     WARN_NOT_OK(s, Substitute( | 
| 215 | 0 |         "Failed to determine if path is a directory: $0", path_)); | 
| 216 | 0 |     if (!s.ok()) { | 
| 217 | 0 |       return; | 
| 218 | 0 |     } | 
| 219 | 0 |     if (is_dir) { | 
| 220 | 0 |       WARN_NOT_OK(env_->DeleteDir(path_), | 
| 221 | 0 |                   Substitute("Failed to remove directory: $0", path_)); | 
| 222 | 0 |     } else { | 
| 223 | 0 |       WARN_NOT_OK(env_->DeleteFile(path_), | 
| 224 | 0 |           Substitute("Failed to remove file: $0", path_)); | 
| 225 | 0 |     } | 
| 226 | 0 |   } | 
| 227 | 1.21M | } | 
| 228 |  |  | 
| 229 |  | } // namespace env_util | 
| 230 |  | } // namespace yb |