• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright 2015 The TensorFlow Authors. All Rights Reserved.
2 
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6 
7     http://www.apache.org/licenses/LICENSE-2.0
8 
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15 
16 #include <sys/stat.h>
17 #include <deque>
18 #include <utility>
19 #include <vector>
20 #if defined(__APPLE__)
21 #include <mach-o/dyld.h>
22 #endif
23 #if defined(__FreeBSD__)
24 #include <sys/sysctl.h>
25 #include <sys/types.h>
26 #endif
27 #if defined(PLATFORM_WINDOWS)
28 #include <windows.h>
29 #include "tensorflow/core/platform/windows/wide_char.h"
30 #define PATH_MAX MAX_PATH
31 #else
32 #include <fcntl.h>
33 #include <string.h>
34 #include <sys/types.h>
35 #include <unistd.h>
36 #endif
37 
38 #include "tensorflow/core/lib/core/errors.h"
39 #include "tensorflow/core/lib/gtl/stl_util.h"
40 #include "tensorflow/core/lib/io/path.h"
41 #include "tensorflow/core/lib/strings/stringprintf.h"
42 #include "tensorflow/core/platform/env.h"
43 #include "tensorflow/core/platform/env_time.h"
44 #include "tensorflow/core/platform/host_info.h"
45 #include "tensorflow/core/platform/protobuf.h"
46 
47 namespace tensorflow {
48 
49 // 128KB copy buffer
50 constexpr size_t kCopyFileBufferSize = 128 * 1024;
51 
52 class FileSystemRegistryImpl : public FileSystemRegistry {
53  public:
54   Status Register(const string& scheme, Factory factory) override;
55   FileSystem* Lookup(const string& scheme) override;
56   Status GetRegisteredFileSystemSchemes(std::vector<string>* schemes) override;
57 
58  private:
59   mutable mutex mu_;
60   mutable std::unordered_map<string, std::unique_ptr<FileSystem>> registry_
61       GUARDED_BY(mu_);
62 };
63 
Register(const string & scheme,FileSystemRegistry::Factory factory)64 Status FileSystemRegistryImpl::Register(const string& scheme,
65                                         FileSystemRegistry::Factory factory) {
66   mutex_lock lock(mu_);
67   if (!registry_.emplace(string(scheme), std::unique_ptr<FileSystem>(factory()))
68            .second) {
69     return errors::AlreadyExists("File factory for ", scheme,
70                                  " already registered");
71   }
72   return Status::OK();
73 }
74 
Lookup(const string & scheme)75 FileSystem* FileSystemRegistryImpl::Lookup(const string& scheme) {
76   mutex_lock lock(mu_);
77   const auto found = registry_.find(scheme);
78   if (found == registry_.end()) {
79     return nullptr;
80   }
81   return found->second.get();
82 }
83 
GetRegisteredFileSystemSchemes(std::vector<string> * schemes)84 Status FileSystemRegistryImpl::GetRegisteredFileSystemSchemes(
85     std::vector<string>* schemes) {
86   mutex_lock lock(mu_);
87   for (const auto& e : registry_) {
88     schemes->push_back(e.first);
89   }
90   return Status::OK();
91 }
92 
Env()93 Env::Env() : file_system_registry_(new FileSystemRegistryImpl) {}
94 
GetFileSystemForFile(const string & fname,FileSystem ** result)95 Status Env::GetFileSystemForFile(const string& fname, FileSystem** result) {
96   StringPiece scheme, host, path;
97   io::ParseURI(fname, &scheme, &host, &path);
98   FileSystem* file_system = file_system_registry_->Lookup(string(scheme));
99   if (!file_system) {
100     if (scheme.empty()) {
101       scheme = "[local]";
102     }
103 
104     return errors::Unimplemented("File system scheme '", scheme,
105                                  "' not implemented (file: '", fname, "')");
106   }
107   *result = file_system;
108   return Status::OK();
109 }
110 
GetRegisteredFileSystemSchemes(std::vector<string> * schemes)111 Status Env::GetRegisteredFileSystemSchemes(std::vector<string>* schemes) {
112   return file_system_registry_->GetRegisteredFileSystemSchemes(schemes);
113 }
114 
RegisterFileSystem(const string & scheme,FileSystemRegistry::Factory factory)115 Status Env::RegisterFileSystem(const string& scheme,
116                                FileSystemRegistry::Factory factory) {
117   return file_system_registry_->Register(scheme, std::move(factory));
118 }
119 
FlushFileSystemCaches()120 Status Env::FlushFileSystemCaches() {
121   std::vector<string> schemes;
122   TF_RETURN_IF_ERROR(GetRegisteredFileSystemSchemes(&schemes));
123   for (const string& scheme : schemes) {
124     FileSystem* fs = nullptr;
125     TF_RETURN_IF_ERROR(
126         GetFileSystemForFile(io::CreateURI(scheme, "", ""), &fs));
127     fs->FlushCaches();
128   }
129   return Status::OK();
130 }
131 
NewRandomAccessFile(const string & fname,std::unique_ptr<RandomAccessFile> * result)132 Status Env::NewRandomAccessFile(const string& fname,
133                                 std::unique_ptr<RandomAccessFile>* result) {
134   FileSystem* fs;
135   TF_RETURN_IF_ERROR(GetFileSystemForFile(fname, &fs));
136   return fs->NewRandomAccessFile(fname, result);
137 }
138 
NewReadOnlyMemoryRegionFromFile(const string & fname,std::unique_ptr<ReadOnlyMemoryRegion> * result)139 Status Env::NewReadOnlyMemoryRegionFromFile(
140     const string& fname, std::unique_ptr<ReadOnlyMemoryRegion>* result) {
141   FileSystem* fs;
142   TF_RETURN_IF_ERROR(GetFileSystemForFile(fname, &fs));
143   return fs->NewReadOnlyMemoryRegionFromFile(fname, result);
144 }
145 
NewWritableFile(const string & fname,std::unique_ptr<WritableFile> * result)146 Status Env::NewWritableFile(const string& fname,
147                             std::unique_ptr<WritableFile>* result) {
148   FileSystem* fs;
149   TF_RETURN_IF_ERROR(GetFileSystemForFile(fname, &fs));
150   return fs->NewWritableFile(fname, result);
151 }
152 
NewAppendableFile(const string & fname,std::unique_ptr<WritableFile> * result)153 Status Env::NewAppendableFile(const string& fname,
154                               std::unique_ptr<WritableFile>* result) {
155   FileSystem* fs;
156   TF_RETURN_IF_ERROR(GetFileSystemForFile(fname, &fs));
157   return fs->NewAppendableFile(fname, result);
158 }
159 
FileExists(const string & fname)160 Status Env::FileExists(const string& fname) {
161   FileSystem* fs;
162   TF_RETURN_IF_ERROR(GetFileSystemForFile(fname, &fs));
163   return fs->FileExists(fname);
164 }
165 
FilesExist(const std::vector<string> & files,std::vector<Status> * status)166 bool Env::FilesExist(const std::vector<string>& files,
167                      std::vector<Status>* status) {
168   std::unordered_map<string, std::vector<string>> files_per_fs;
169   for (const auto& file : files) {
170     StringPiece scheme, host, path;
171     io::ParseURI(file, &scheme, &host, &path);
172     files_per_fs[string(scheme)].push_back(file);
173   }
174 
175   std::unordered_map<string, Status> per_file_status;
176   bool result = true;
177   for (auto itr : files_per_fs) {
178     FileSystem* file_system = file_system_registry_->Lookup(itr.first);
179     bool fs_result;
180     std::vector<Status> local_status;
181     std::vector<Status>* fs_status = status ? &local_status : nullptr;
182     if (!file_system) {
183       fs_result = false;
184       if (fs_status) {
185         Status s = errors::Unimplemented("File system scheme '", itr.first,
186                                          "' not implemented");
187         local_status.resize(itr.second.size(), s);
188       }
189     } else {
190       fs_result = file_system->FilesExist(itr.second, fs_status);
191     }
192     if (fs_status) {
193       result &= fs_result;
194       for (int i = 0; i < itr.second.size(); ++i) {
195         per_file_status[itr.second[i]] = fs_status->at(i);
196       }
197     } else if (!fs_result) {
198       // Return early
199       return false;
200     }
201   }
202 
203   if (status) {
204     for (const auto& file : files) {
205       status->push_back(per_file_status[file]);
206     }
207   }
208 
209   return result;
210 }
211 
GetChildren(const string & dir,std::vector<string> * result)212 Status Env::GetChildren(const string& dir, std::vector<string>* result) {
213   FileSystem* fs;
214   TF_RETURN_IF_ERROR(GetFileSystemForFile(dir, &fs));
215   return fs->GetChildren(dir, result);
216 }
217 
GetMatchingPaths(const string & pattern,std::vector<string> * results)218 Status Env::GetMatchingPaths(const string& pattern,
219                              std::vector<string>* results) {
220   FileSystem* fs;
221   TF_RETURN_IF_ERROR(GetFileSystemForFile(pattern, &fs));
222   return fs->GetMatchingPaths(pattern, results);
223 }
224 
DeleteFile(const string & fname)225 Status Env::DeleteFile(const string& fname) {
226   FileSystem* fs;
227   TF_RETURN_IF_ERROR(GetFileSystemForFile(fname, &fs));
228   return fs->DeleteFile(fname);
229 }
230 
RecursivelyCreateDir(const string & dirname)231 Status Env::RecursivelyCreateDir(const string& dirname) {
232   FileSystem* fs;
233   TF_RETURN_IF_ERROR(GetFileSystemForFile(dirname, &fs));
234   return fs->RecursivelyCreateDir(dirname);
235 }
236 
CreateDir(const string & dirname)237 Status Env::CreateDir(const string& dirname) {
238   FileSystem* fs;
239   TF_RETURN_IF_ERROR(GetFileSystemForFile(dirname, &fs));
240   return fs->CreateDir(dirname);
241 }
242 
DeleteDir(const string & dirname)243 Status Env::DeleteDir(const string& dirname) {
244   FileSystem* fs;
245   TF_RETURN_IF_ERROR(GetFileSystemForFile(dirname, &fs));
246   return fs->DeleteDir(dirname);
247 }
248 
Stat(const string & fname,FileStatistics * stat)249 Status Env::Stat(const string& fname, FileStatistics* stat) {
250   FileSystem* fs;
251   TF_RETURN_IF_ERROR(GetFileSystemForFile(fname, &fs));
252   return fs->Stat(fname, stat);
253 }
254 
IsDirectory(const string & fname)255 Status Env::IsDirectory(const string& fname) {
256   FileSystem* fs;
257   TF_RETURN_IF_ERROR(GetFileSystemForFile(fname, &fs));
258   return fs->IsDirectory(fname);
259 }
260 
DeleteRecursively(const string & dirname,int64 * undeleted_files,int64 * undeleted_dirs)261 Status Env::DeleteRecursively(const string& dirname, int64* undeleted_files,
262                               int64* undeleted_dirs) {
263   FileSystem* fs;
264   TF_RETURN_IF_ERROR(GetFileSystemForFile(dirname, &fs));
265   return fs->DeleteRecursively(dirname, undeleted_files, undeleted_dirs);
266 }
267 
GetFileSize(const string & fname,uint64 * file_size)268 Status Env::GetFileSize(const string& fname, uint64* file_size) {
269   FileSystem* fs;
270   TF_RETURN_IF_ERROR(GetFileSystemForFile(fname, &fs));
271   return fs->GetFileSize(fname, file_size);
272 }
273 
RenameFile(const string & src,const string & target)274 Status Env::RenameFile(const string& src, const string& target) {
275   FileSystem* src_fs;
276   FileSystem* target_fs;
277   TF_RETURN_IF_ERROR(GetFileSystemForFile(src, &src_fs));
278   TF_RETURN_IF_ERROR(GetFileSystemForFile(target, &target_fs));
279   if (src_fs != target_fs) {
280     return errors::Unimplemented("Renaming ", src, " to ", target,
281                                  " not implemented");
282   }
283   return src_fs->RenameFile(src, target);
284 }
285 
CopyFile(const string & src,const string & target)286 Status Env::CopyFile(const string& src, const string& target) {
287   FileSystem* src_fs;
288   FileSystem* target_fs;
289   TF_RETURN_IF_ERROR(GetFileSystemForFile(src, &src_fs));
290   TF_RETURN_IF_ERROR(GetFileSystemForFile(target, &target_fs));
291   if (src_fs == target_fs) {
292     return src_fs->CopyFile(src, target);
293   }
294   return FileSystemCopyFile(src_fs, src, target_fs, target);
295 }
296 
GetExecutablePath()297 string Env::GetExecutablePath() {
298   char exe_path[PATH_MAX] = {0};
299 #ifdef __APPLE__
300   uint32_t buffer_size(0U);
301   _NSGetExecutablePath(nullptr, &buffer_size);
302   char unresolved_path[buffer_size];
303   _NSGetExecutablePath(unresolved_path, &buffer_size);
304   CHECK(realpath(unresolved_path, exe_path));
305 #elif defined(__FreeBSD__)
306   int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
307   size_t exe_path_size = PATH_MAX;
308 
309   if (sysctl(mib, 4, exe_path, &exe_path_size, NULL, 0) != 0) {
310     // Resolution of path failed
311     return "";
312   }
313 #elif defined(PLATFORM_WINDOWS)
314   HMODULE hModule = GetModuleHandleW(NULL);
315   WCHAR wc_file_path[MAX_PATH] = {0};
316   GetModuleFileNameW(hModule, wc_file_path, MAX_PATH);
317   string file_path = WideCharToUtf8(wc_file_path);
318   std::copy(file_path.begin(), file_path.end(), exe_path);
319 #else
320   char buf[PATH_MAX] = {0};
321   int path_length = readlink("/proc/self/exe", buf, sizeof(buf) - 1);
322   CHECK_NE(-1, path_length);
323 
324   if (strstr(buf, "python") != nullptr) {
325     // Discard the path of the python binary, and any flags.
326     int fd = open("/proc/self/cmdline", O_RDONLY);
327     int cmd_length = read(fd, buf, PATH_MAX - 1);
328     CHECK_NE(-1, cmd_length);
329     int token_pos = 0;
330     for (bool token_is_first_or_flag = true; token_is_first_or_flag;) {
331       // Get token length, including null
332       int token_len = strlen(&buf[token_pos]) + 1;
333       token_is_first_or_flag = false;
334       // Check if we can skip without overshooting
335       if (token_pos + token_len < cmd_length) {
336         token_pos += token_len;
337         token_is_first_or_flag = (buf[token_pos] == '-');  // token is a flag
338       }
339     }
340     snprintf(exe_path, sizeof(exe_path), "%s", &buf[token_pos]);
341   } else {
342     snprintf(exe_path, sizeof(exe_path), "%s", buf);
343   }
344 
345 #endif
346   // Make sure it's null-terminated:
347   exe_path[sizeof(exe_path) - 1] = 0;
348 
349   return exe_path;
350 }
351 
LocalTempFilename(string * filename)352 bool Env::LocalTempFilename(string* filename) {
353   std::vector<string> dirs;
354   GetLocalTempDirectories(&dirs);
355 
356   // Try each directory, as they might be full, have inappropriate
357   // permissions or have different problems at times.
358   for (const string& dir : dirs) {
359     *filename = io::JoinPath(dir, "tempfile-");
360     if (CreateUniqueFileName(filename, "")) {
361       return true;
362     }
363   }
364   return false;
365 }
366 
CreateUniqueFileName(string * prefix,const string & suffix)367 bool Env::CreateUniqueFileName(string* prefix, const string& suffix) {
368   int32 tid = GetCurrentThreadId();
369 #ifdef PLATFORM_WINDOWS
370   int32 pid = static_cast<int32>(GetCurrentProcessId());
371 #else
372   int32 pid = static_cast<int32>(getpid());
373 #endif
374   uint64 now_microsec = NowMicros();
375 
376   *prefix += strings::Printf("%s-%x-%d-%llx", port::Hostname().c_str(), tid,
377                              pid, now_microsec);
378 
379   if (!suffix.empty()) {
380     *prefix += suffix;
381   }
382   if (FileExists(*prefix).ok()) {
383     prefix->clear();
384     return false;
385   } else {
386     return true;
387   }
388 }
389 
~Thread()390 Thread::~Thread() {}
391 
~EnvWrapper()392 EnvWrapper::~EnvWrapper() {}
393 
ReadFileToString(Env * env,const string & fname,string * data)394 Status ReadFileToString(Env* env, const string& fname, string* data) {
395   uint64 file_size;
396   Status s = env->GetFileSize(fname, &file_size);
397   if (!s.ok()) {
398     return s;
399   }
400   std::unique_ptr<RandomAccessFile> file;
401   s = env->NewRandomAccessFile(fname, &file);
402   if (!s.ok()) {
403     return s;
404   }
405   gtl::STLStringResizeUninitialized(data, file_size);
406   char* p = gtl::string_as_array(data);
407   StringPiece result;
408   s = file->Read(0, file_size, &result, p);
409   if (!s.ok()) {
410     data->clear();
411   } else if (result.size() != file_size) {
412     s = errors::Aborted("File ", fname, " changed while reading: ", file_size,
413                         " vs. ", result.size());
414     data->clear();
415   } else if (result.data() == p) {
416     // Data is already in the correct location
417   } else {
418     memmove(p, result.data(), result.size());
419   }
420   return s;
421 }
422 
WriteStringToFile(Env * env,const string & fname,const StringPiece & data)423 Status WriteStringToFile(Env* env, const string& fname,
424                          const StringPiece& data) {
425   std::unique_ptr<WritableFile> file;
426   Status s = env->NewWritableFile(fname, &file);
427   if (!s.ok()) {
428     return s;
429   }
430   s = file->Append(data);
431   if (s.ok()) {
432     s = file->Close();
433   }
434   return s;
435 }
436 
FileSystemCopyFile(FileSystem * src_fs,const string & src,FileSystem * target_fs,const string & target)437 Status FileSystemCopyFile(FileSystem* src_fs, const string& src,
438                           FileSystem* target_fs, const string& target) {
439   std::unique_ptr<RandomAccessFile> src_file;
440   TF_RETURN_IF_ERROR(src_fs->NewRandomAccessFile(src, &src_file));
441 
442   std::unique_ptr<WritableFile> target_file;
443   TF_RETURN_IF_ERROR(target_fs->NewWritableFile(target, &target_file));
444 
445   uint64 offset = 0;
446   std::unique_ptr<char[]> scratch(new char[kCopyFileBufferSize]);
447   Status s = Status::OK();
448   while (s.ok()) {
449     StringPiece result;
450     s = src_file->Read(offset, kCopyFileBufferSize, &result, scratch.get());
451     if (!(s.ok() || s.code() == error::OUT_OF_RANGE)) {
452       return s;
453     }
454     TF_RETURN_IF_ERROR(target_file->Append(result));
455     offset += result.size();
456   }
457   return target_file->Close();
458 }
459 
460 // A ZeroCopyInputStream on a RandomAccessFile.
461 namespace {
462 class FileStream : public ::tensorflow::protobuf::io::ZeroCopyInputStream {
463  public:
FileStream(RandomAccessFile * file)464   explicit FileStream(RandomAccessFile* file) : file_(file), pos_(0) {}
465 
BackUp(int count)466   void BackUp(int count) override { pos_ -= count; }
Skip(int count)467   bool Skip(int count) override {
468     pos_ += count;
469     return true;
470   }
ByteCount() const471   protobuf_int64 ByteCount() const override { return pos_; }
status() const472   Status status() const { return status_; }
473 
Next(const void ** data,int * size)474   bool Next(const void** data, int* size) override {
475     StringPiece result;
476     Status s = file_->Read(pos_, kBufSize, &result, scratch_);
477     if (result.empty()) {
478       status_ = s;
479       return false;
480     }
481     pos_ += result.size();
482     *data = result.data();
483     *size = result.size();
484     return true;
485   }
486 
487  private:
488   static const int kBufSize = 512 << 10;
489 
490   RandomAccessFile* file_;
491   int64 pos_;
492   Status status_;
493   char scratch_[kBufSize];
494 };
495 
496 }  // namespace
497 
WriteBinaryProto(Env * env,const string & fname,const::tensorflow::protobuf::MessageLite & proto)498 Status WriteBinaryProto(Env* env, const string& fname,
499                         const ::tensorflow::protobuf::MessageLite& proto) {
500   string serialized;
501   proto.AppendToString(&serialized);
502   return WriteStringToFile(env, fname, serialized);
503 }
504 
ReadBinaryProto(Env * env,const string & fname,::tensorflow::protobuf::MessageLite * proto)505 Status ReadBinaryProto(Env* env, const string& fname,
506                        ::tensorflow::protobuf::MessageLite* proto) {
507   std::unique_ptr<RandomAccessFile> file;
508   TF_RETURN_IF_ERROR(env->NewRandomAccessFile(fname, &file));
509   std::unique_ptr<FileStream> stream(new FileStream(file.get()));
510 
511   // TODO(jiayq): the following coded stream is for debugging purposes to allow
512   // one to parse arbitrarily large messages for MessageLite. One most likely
513   // doesn't want to put protobufs larger than 64MB on Android, so we should
514   // eventually remove this and quit loud when a large protobuf is passed in.
515   ::tensorflow::protobuf::io::CodedInputStream coded_stream(stream.get());
516   // Total bytes hard limit / warning limit are set to 1GB and 512MB
517   // respectively.
518   coded_stream.SetTotalBytesLimit(1024LL << 20, 512LL << 20);
519 
520   if (!proto->ParseFromCodedStream(&coded_stream)) {
521     TF_RETURN_IF_ERROR(stream->status());
522     return errors::DataLoss("Can't parse ", fname, " as binary proto");
523   }
524   return Status::OK();
525 }
526 
WriteTextProto(Env * env,const string & fname,const::tensorflow::protobuf::Message & proto)527 Status WriteTextProto(Env* env, const string& fname,
528                       const ::tensorflow::protobuf::Message& proto) {
529 #if !defined(TENSORFLOW_LITE_PROTOS)
530   string serialized;
531   if (!::tensorflow::protobuf::TextFormat::PrintToString(proto, &serialized)) {
532     return errors::FailedPrecondition("Unable to convert proto to text.");
533   }
534   return WriteStringToFile(env, fname, serialized);
535 #else
536   return errors::Unimplemented("Can't write text protos with protolite.");
537 #endif
538 }
539 
ReadTextProto(Env * env,const string & fname,::tensorflow::protobuf::Message * proto)540 Status ReadTextProto(Env* env, const string& fname,
541                      ::tensorflow::protobuf::Message* proto) {
542 #if !defined(TENSORFLOW_LITE_PROTOS)
543   std::unique_ptr<RandomAccessFile> file;
544   TF_RETURN_IF_ERROR(env->NewRandomAccessFile(fname, &file));
545   std::unique_ptr<FileStream> stream(new FileStream(file.get()));
546 
547   if (!::tensorflow::protobuf::TextFormat::Parse(stream.get(), proto)) {
548     TF_RETURN_IF_ERROR(stream->status());
549     return errors::DataLoss("Can't parse ", fname, " as text proto");
550   }
551   return Status::OK();
552 #else
553   return errors::Unimplemented("Can't parse text protos with protolite.");
554 #endif
555 }
556 
557 }  // namespace tensorflow
558