env.cc revision 667a023a417f36ff4eabaee77daf374dceaa5ddd
1/* Copyright 2015 The TensorFlow Authors. All Rights Reserved. 2 3Licensed under the Apache License, Version 2.0 (the "License"); 4you may not use this file except in compliance with the License. 5You may obtain a copy of the License at 6 7 http://www.apache.org/licenses/LICENSE-2.0 8 9Unless required by applicable law or agreed to in writing, software 10distributed under the License is distributed on an "AS IS" BASIS, 11WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12See the License for the specific language governing permissions and 13limitations under the License. 14==============================================================================*/ 15 16#include <deque> 17#include <vector> 18#if defined(__APPLE__) 19#include <mach-o/dyld.h> 20#endif 21#if defined(PLATFORM_WINDOWS) 22#include <windows.h> 23#define PATH_MAX MAX_PATH 24#else 25#include <unistd.h> 26#endif 27 28#include "tensorflow/core/lib/core/errors.h" 29#include "tensorflow/core/lib/gtl/map_util.h" 30#include "tensorflow/core/lib/gtl/stl_util.h" 31#include "tensorflow/core/lib/io/path.h" 32#include "tensorflow/core/platform/env.h" 33#include "tensorflow/core/platform/protobuf.h" 34 35namespace tensorflow { 36 37class FileSystemRegistryImpl : public FileSystemRegistry { 38 public: 39 Status Register(const string& scheme, Factory factory) override; 40 FileSystem* Lookup(const string& scheme) override; 41 Status GetRegisteredFileSystemSchemes(std::vector<string>* schemes) override; 42 43 private: 44 mutable mutex mu_; 45 mutable std::unordered_map<string, std::unique_ptr<FileSystem>> registry_ 46 GUARDED_BY(mu_); 47}; 48 49Status FileSystemRegistryImpl::Register(const string& scheme, 50 FileSystemRegistry::Factory factory) { 51 mutex_lock lock(mu_); 52 if (!registry_.emplace(string(scheme), std::unique_ptr<FileSystem>(factory())) 53 .second) { 54 return errors::AlreadyExists("File factory for ", scheme, 55 " already registered"); 56 } 57 return Status::OK(); 58} 59 60FileSystem* FileSystemRegistryImpl::Lookup(const string& scheme) { 61 mutex_lock lock(mu_); 62 const auto found = registry_.find(scheme); 63 if (found == registry_.end()) { 64 return nullptr; 65 } 66 return found->second.get(); 67} 68 69Status FileSystemRegistryImpl::GetRegisteredFileSystemSchemes( 70 std::vector<string>* schemes) { 71 mutex_lock lock(mu_); 72 for (const auto& e : registry_) { 73 schemes->push_back(e.first); 74 } 75 return Status::OK(); 76} 77 78Env::Env() : file_system_registry_(new FileSystemRegistryImpl) {} 79 80Status Env::GetFileSystemForFile(const string& fname, FileSystem** result) { 81 StringPiece scheme, host, path; 82 io::ParseURI(fname, &scheme, &host, &path); 83 FileSystem* file_system = file_system_registry_->Lookup(scheme.ToString()); 84 if (!file_system) { 85 return errors::Unimplemented("File system scheme ", scheme, 86 " not implemented"); 87 } 88 *result = file_system; 89 return Status::OK(); 90} 91 92Status Env::GetRegisteredFileSystemSchemes(std::vector<string>* schemes) { 93 return file_system_registry_->GetRegisteredFileSystemSchemes(schemes); 94} 95 96Status Env::RegisterFileSystem(const string& scheme, 97 FileSystemRegistry::Factory factory) { 98 return file_system_registry_->Register(scheme, factory); 99} 100 101Status Env::NewRandomAccessFile(const string& fname, 102 std::unique_ptr<RandomAccessFile>* result) { 103 FileSystem* fs; 104 TF_RETURN_IF_ERROR(GetFileSystemForFile(fname, &fs)); 105 return fs->NewRandomAccessFile(fname, result); 106} 107 108Status Env::NewReadOnlyMemoryRegionFromFile( 109 const string& fname, std::unique_ptr<ReadOnlyMemoryRegion>* result) { 110 FileSystem* fs; 111 TF_RETURN_IF_ERROR(GetFileSystemForFile(fname, &fs)); 112 return fs->NewReadOnlyMemoryRegionFromFile(fname, result); 113} 114 115Status Env::NewWritableFile(const string& fname, 116 std::unique_ptr<WritableFile>* result) { 117 FileSystem* fs; 118 TF_RETURN_IF_ERROR(GetFileSystemForFile(fname, &fs)); 119 return fs->NewWritableFile(fname, result); 120} 121 122Status Env::NewAppendableFile(const string& fname, 123 std::unique_ptr<WritableFile>* result) { 124 FileSystem* fs; 125 TF_RETURN_IF_ERROR(GetFileSystemForFile(fname, &fs)); 126 return fs->NewAppendableFile(fname, result); 127} 128 129Status Env::FileExists(const string& fname) { 130 FileSystem* fs; 131 TF_RETURN_IF_ERROR(GetFileSystemForFile(fname, &fs)); 132 return fs->FileExists(fname); 133} 134 135Status Env::GetChildren(const string& dir, std::vector<string>* result) { 136 FileSystem* fs; 137 TF_RETURN_IF_ERROR(GetFileSystemForFile(dir, &fs)); 138 return fs->GetChildren(dir, result); 139} 140 141Status Env::GetMatchingPaths(const string& pattern, 142 std::vector<string>* results) { 143 FileSystem* fs; 144 TF_RETURN_IF_ERROR(GetFileSystemForFile(pattern, &fs)); 145 return fs->GetMatchingPaths(pattern, results); 146} 147 148Status Env::DeleteFile(const string& fname) { 149 FileSystem* fs; 150 TF_RETURN_IF_ERROR(GetFileSystemForFile(fname, &fs)); 151 return fs->DeleteFile(fname); 152} 153 154Status Env::RecursivelyCreateDir(const string& dirname) { 155 FileSystem* fs; 156 TF_RETURN_IF_ERROR(GetFileSystemForFile(dirname, &fs)); 157 return fs->RecursivelyCreateDir(dirname); 158} 159 160Status Env::CreateDir(const string& dirname) { 161 FileSystem* fs; 162 TF_RETURN_IF_ERROR(GetFileSystemForFile(dirname, &fs)); 163 return fs->CreateDir(dirname); 164} 165 166Status Env::DeleteDir(const string& dirname) { 167 FileSystem* fs; 168 TF_RETURN_IF_ERROR(GetFileSystemForFile(dirname, &fs)); 169 return fs->DeleteDir(dirname); 170} 171 172Status Env::Stat(const string& fname, FileStatistics* stat) { 173 FileSystem* fs; 174 TF_RETURN_IF_ERROR(GetFileSystemForFile(fname, &fs)); 175 return fs->Stat(fname, stat); 176} 177 178Status Env::IsDirectory(const string& fname) { 179 FileSystem* fs; 180 TF_RETURN_IF_ERROR(GetFileSystemForFile(fname, &fs)); 181 return fs->IsDirectory(fname); 182} 183 184Status Env::DeleteRecursively(const string& dirname, int64* undeleted_files, 185 int64* undeleted_dirs) { 186 FileSystem* fs; 187 TF_RETURN_IF_ERROR(GetFileSystemForFile(dirname, &fs)); 188 return fs->DeleteRecursively(dirname, undeleted_files, undeleted_dirs); 189} 190 191Status Env::GetFileSize(const string& fname, uint64* file_size) { 192 FileSystem* fs; 193 TF_RETURN_IF_ERROR(GetFileSystemForFile(fname, &fs)); 194 return fs->GetFileSize(fname, file_size); 195} 196 197Status Env::RenameFile(const string& src, const string& target) { 198 FileSystem* src_fs; 199 FileSystem* target_fs; 200 TF_RETURN_IF_ERROR(GetFileSystemForFile(src, &src_fs)); 201 TF_RETURN_IF_ERROR(GetFileSystemForFile(target, &target_fs)); 202 if (src_fs != target_fs) { 203 return errors::Unimplemented("Renaming ", src, " to ", target, 204 " not implemented"); 205 } 206 return src_fs->RenameFile(src, target); 207} 208 209string Env::GetExecutablePath() { 210 char exe_path[PATH_MAX] = {0}; 211#ifdef __APPLE__ 212 uint32_t buffer_size(0U); 213 _NSGetExecutablePath(nullptr, &buffer_size); 214 char unresolved_path[buffer_size]; 215 _NSGetExecutablePath(unresolved_path, &buffer_size); 216 CHECK(realpath(unresolved_path, exe_path)); 217#elif defined(PLATFORM_WINDOWS) 218 HMODULE hModule = GetModuleHandle(NULL); 219 GetModuleFileName(hModule, exe_path, MAX_PATH); 220#else 221 CHECK_NE(-1, readlink("/proc/self/exe", exe_path, sizeof(exe_path) - 1)); 222#endif 223 // Make sure it's null-terminated: 224 exe_path[sizeof(exe_path) - 1] = 0; 225 226 return exe_path; 227} 228 229Thread::~Thread() {} 230 231EnvWrapper::~EnvWrapper() {} 232 233Status ReadFileToString(Env* env, const string& fname, string* data) { 234 uint64 file_size; 235 Status s = env->GetFileSize(fname, &file_size); 236 if (!s.ok()) { 237 return s; 238 } 239 std::unique_ptr<RandomAccessFile> file; 240 s = env->NewRandomAccessFile(fname, &file); 241 if (!s.ok()) { 242 return s; 243 } 244 gtl::STLStringResizeUninitialized(data, file_size); 245 char* p = gtl::string_as_array(data); 246 StringPiece result; 247 s = file->Read(0, file_size, &result, p); 248 if (!s.ok()) { 249 data->clear(); 250 } else if (result.size() != file_size) { 251 s = errors::Aborted("File ", fname, " changed while reading: ", file_size, 252 " vs. ", result.size()); 253 data->clear(); 254 } else if (result.data() == p) { 255 // Data is already in the correct location 256 } else { 257 memmove(p, result.data(), result.size()); 258 } 259 return s; 260} 261 262Status WriteStringToFile(Env* env, const string& fname, 263 const StringPiece& data) { 264 std::unique_ptr<WritableFile> file; 265 Status s = env->NewWritableFile(fname, &file); 266 if (!s.ok()) { 267 return s; 268 } 269 s = file->Append(data); 270 if (s.ok()) { 271 s = file->Close(); 272 } 273 return s; 274} 275 276// A ZeroCopyInputStream on a RandomAccessFile. 277namespace { 278class FileStream : public ::tensorflow::protobuf::io::ZeroCopyInputStream { 279 public: 280 explicit FileStream(RandomAccessFile* file) : file_(file), pos_(0) {} 281 282 void BackUp(int count) override { pos_ -= count; } 283 bool Skip(int count) override { 284 pos_ += count; 285 return true; 286 } 287 protobuf_int64 ByteCount() const override { return pos_; } 288 Status status() const { return status_; } 289 290 bool Next(const void** data, int* size) override { 291 StringPiece result; 292 Status s = file_->Read(pos_, kBufSize, &result, scratch_); 293 if (result.empty()) { 294 status_ = s; 295 return false; 296 } 297 pos_ += result.size(); 298 *data = result.data(); 299 *size = result.size(); 300 return true; 301 } 302 303 private: 304 static const int kBufSize = 512 << 10; 305 306 RandomAccessFile* file_; 307 int64 pos_; 308 Status status_; 309 char scratch_[kBufSize]; 310}; 311 312} // namespace 313 314Status WriteBinaryProto(Env* env, const string& fname, 315 const ::tensorflow::protobuf::MessageLite& proto) { 316 string serialized; 317 proto.AppendToString(&serialized); 318 return WriteStringToFile(env, fname, serialized); 319} 320 321Status ReadBinaryProto(Env* env, const string& fname, 322 ::tensorflow::protobuf::MessageLite* proto) { 323 std::unique_ptr<RandomAccessFile> file; 324 TF_RETURN_IF_ERROR(env->NewRandomAccessFile(fname, &file)); 325 std::unique_ptr<FileStream> stream(new FileStream(file.get())); 326 327 // TODO(jiayq): the following coded stream is for debugging purposes to allow 328 // one to parse arbitrarily large messages for MessageLite. One most likely 329 // doesn't want to put protobufs larger than 64MB on Android, so we should 330 // eventually remove this and quit loud when a large protobuf is passed in. 331 ::tensorflow::protobuf::io::CodedInputStream coded_stream(stream.get()); 332 // Total bytes hard limit / warning limit are set to 1GB and 512MB 333 // respectively. 334 coded_stream.SetTotalBytesLimit(1024LL << 20, 512LL << 20); 335 336 if (!proto->ParseFromCodedStream(&coded_stream)) { 337 TF_RETURN_IF_ERROR(stream->status()); 338 return errors::DataLoss("Can't parse ", fname, " as binary proto"); 339 } 340 return Status::OK(); 341} 342 343Status WriteTextProto(Env* env, const string& fname, 344 const ::tensorflow::protobuf::Message& proto) { 345#if !defined(TENSORFLOW_LITE_PROTOS) 346 string serialized; 347 if (!::tensorflow::protobuf::TextFormat::PrintToString(proto, &serialized)) { 348 return errors::FailedPrecondition("Unable to convert proto to text."); 349 } 350 return WriteStringToFile(env, fname, serialized); 351#else 352 return errors::Unimplemented("Can't write text protos with protolite."); 353#endif 354} 355 356Status ReadTextProto(Env* env, const string& fname, 357 ::tensorflow::protobuf::Message* proto) { 358#if !defined(TENSORFLOW_LITE_PROTOS) 359 std::unique_ptr<RandomAccessFile> file; 360 TF_RETURN_IF_ERROR(env->NewRandomAccessFile(fname, &file)); 361 std::unique_ptr<FileStream> stream(new FileStream(file.get())); 362 363 if (!::tensorflow::protobuf::TextFormat::Parse(stream.get(), proto)) { 364 TF_RETURN_IF_ERROR(stream->status()); 365 return errors::DataLoss("Can't parse ", fname, " as text proto"); 366 } 367 return Status::OK(); 368#else 369 return errors::Unimplemented("Can't parse text protos with protolite."); 370#endif 371} 372 373} // namespace tensorflow 374