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