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