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