aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--tensorflow/core/BUILD3
-rw-r--r--tensorflow/core/kernels/immutable_constant_op_test.cc31
-rw-r--r--tensorflow/core/platform/env.cc88
-rw-r--r--tensorflow/core/platform/env.h132
-rw-r--r--tensorflow/core/platform/env_test.cc45
-rw-r--r--tensorflow/core/platform/file_system.cc92
-rw-r--r--tensorflow/core/platform/file_system.h243
-rw-r--r--tensorflow/core/platform/posix/env.cc362
-rw-r--r--tensorflow/core/platform/posix/posix_file_system.cc404
-rw-r--r--tensorflow/core/platform/posix/posix_file_system.h65
-rw-r--r--tensorflow/python/BUILD30
-rw-r--r--tensorflow/python/framework/file_system_test.py47
-rw-r--r--tensorflow/python/framework/framework_lib.py1
-rw-r--r--tensorflow/python/framework/load_library.py42
-rw-r--r--tensorflow/python/framework/test_file_system.cc48
-rwxr-xr-xtensorflow/tools/ci_build/builds/test_installation.sh5
16 files changed, 1155 insertions, 483 deletions
diff --git a/tensorflow/core/BUILD b/tensorflow/core/BUILD
index 24451af96f..e7d9ab13a0 100644
--- a/tensorflow/core/BUILD
+++ b/tensorflow/core/BUILD
@@ -172,6 +172,7 @@ cc_library(
"lib/strings/strcat.h",
"lib/strings/stringprintf.h",
"platform/env.h",
+ "platform/file_system.h",
"platform/host_info.h", # TODO(josh11b): make internal
"platform/init_main.h",
"platform/logging.h",
@@ -615,6 +616,7 @@ ANDROID_TF_LITE_HDRS = [
"framework/tensor.h",
"platform/default/integral_types.h",
"platform/env.h",
+ "platform/file_system.h",
"platform/logging.h",
"platform/platform.h",
"platform/types.h",
@@ -882,6 +884,7 @@ filegroup(
"platform/default/protobuf.h",
"platform/default/thread_annotations.h",
"platform/env.h",
+ "platform/file_system.h",
"platform/host_info.h",
"platform/logging.h",
"platform/macros.h",
diff --git a/tensorflow/core/kernels/immutable_constant_op_test.cc b/tensorflow/core/kernels/immutable_constant_op_test.cc
index 7c17022dd5..1dc82a2979 100644
--- a/tensorflow/core/kernels/immutable_constant_op_test.cc
+++ b/tensorflow/core/kernels/immutable_constant_op_test.cc
@@ -54,18 +54,17 @@ class TestReadOnlyMemoryRegion : public ReadOnlyMemoryRegion {
uint64 length_;
};
-// A mock environment class that creates ReadOnlyMemoryRegion from allocated
-// memory.
-class TestEnvironment : public EnvWrapper {
+// A mock file system and environment class that creates ReadOnlyMemoryRegion
+// from allocated memory.
+class TestFileSystem : public NullFileSystem {
public:
- explicit TestEnvironment(Env* env) : EnvWrapper(env) {}
- ~TestEnvironment() override = default;
+ ~TestFileSystem() override = default;
Status NewReadOnlyMemoryRegionFromFile(
const string& fname, ReadOnlyMemoryRegion** result) override {
float val = 0;
// For the tests create in-memory regions with float values equal to the
// first letter of the region name.
- switch (fname.front()) {
+ switch (GetNameFromURI(fname).front()) {
case '2':
val = 2.0f;
break;
@@ -84,20 +83,23 @@ class TestEnvironment : public EnvWrapper {
}
};
+REGISTER_FILE_SYSTEM("test", TestFileSystem);
+
struct ImmutableConstantOpTest {};
TEST(ImmutableConstantOpTest, Simple) {
const TensorShape kTestTensorShape({4, 1});
const TensorShape kTestTensorShapeT({1, 4});
GraphDefBuilder b(GraphDefBuilder::kFailImmediately);
- Node* node1 = ops::ImmutableConst(DT_FLOAT, kTestTensorShape, "2", b.opts());
- Node* node2 = ops::ImmutableConst(DT_FLOAT, kTestTensorShapeT, "3", b.opts());
+ Node* node1 =
+ ops::ImmutableConst(DT_FLOAT, kTestTensorShape, "test://2", b.opts());
+ Node* node2 =
+ ops::ImmutableConst(DT_FLOAT, kTestTensorShapeT, "test://3", b.opts());
Node* result = ops::MatMul(node1, node2, b.opts());
GraphDef graph_def;
TF_ASSERT_OK(b.ToGraphDef(&graph_def));
- std::unique_ptr<Env> env_ptr(new TestEnvironment(Env::Default()));
SessionOptions session_options;
- session_options.env = env_ptr.get();
+ session_options.env = Env::Default();
session_options.config.mutable_graph_options()
->mutable_optimizer_options()
->set_opt_level(OptimizerOptions_Level_L0);
@@ -120,14 +122,15 @@ TEST(ImmutableConstantOpTest, ExecutionError) {
const TensorShape kBadTensorShape({40, 100});
const TensorShape kTestTensorShapeT({1, 4});
GraphDefBuilder b(GraphDefBuilder::kFailImmediately);
- Node* node1 = ops::ImmutableConst(DT_FLOAT, kBadTensorShape, "2", b.opts());
- Node* node2 = ops::ImmutableConst(DT_FLOAT, kTestTensorShapeT, "3", b.opts());
+ Node* node1 =
+ ops::ImmutableConst(DT_FLOAT, kBadTensorShape, "test://2", b.opts());
+ Node* node2 =
+ ops::ImmutableConst(DT_FLOAT, kTestTensorShapeT, "test://3", b.opts());
Node* result = ops::MatMul(node1, node2, b.opts());
GraphDef graph_def;
TF_ASSERT_OK(b.ToGraphDef(&graph_def));
- std::unique_ptr<Env> env_ptr(new TestEnvironment(Env::Default()));
SessionOptions session_options;
- session_options.env = env_ptr.get();
+ session_options.env = Env::Default();
std::unique_ptr<Session> session(NewSession(session_options));
ASSERT_TRUE(session != nullptr) << "Failed to create session";
TF_ASSERT_OK(session->Create(graph_def)) << "Can't create test graph";
diff --git a/tensorflow/core/platform/env.cc b/tensorflow/core/platform/env.cc
index db17f92df5..9f85376001 100644
--- a/tensorflow/core/platform/env.cc
+++ b/tensorflow/core/platform/env.cc
@@ -15,6 +15,7 @@ limitations under the License.
#include "tensorflow/core/platform/env.h"
#include "tensorflow/core/lib/core/errors.h"
+#include "tensorflow/core/lib/gtl/map_util.h"
#include "tensorflow/core/lib/gtl/stl_util.h"
#include "tensorflow/core/platform/protobuf.h"
@@ -22,9 +23,92 @@ namespace tensorflow {
Env::~Env() {}
-RandomAccessFile::~RandomAccessFile() {}
+Status Env::GetFileSystemForFile(const string& fname, FileSystem** result) {
+ string scheme = GetSchemeFromURI(fname);
+ FileSystem* file_system = GlobalFileSystemRegistry()->Lookup(scheme);
+ if (!file_system) {
+ return errors::Unimplemented("File system scheme ", scheme,
+ " not implemented");
+ }
+ *result = file_system;
+ return Status::OK();
+}
+
+Status Env::NewRandomAccessFile(const string& fname,
+ RandomAccessFile** result) {
+ FileSystem* fs;
+ TF_RETURN_IF_ERROR(GetFileSystemForFile(fname, &fs));
+ return fs->NewRandomAccessFile(fname, result);
+}
+
+Status Env::NewWritableFile(const string& fname, WritableFile** result) {
+ FileSystem* fs;
+ TF_RETURN_IF_ERROR(GetFileSystemForFile(fname, &fs));
+ return fs->NewWritableFile(fname, result);
+}
+
+Status Env::NewAppendableFile(const string& fname, WritableFile** result) {
+ FileSystem* fs;
+ TF_RETURN_IF_ERROR(GetFileSystemForFile(fname, &fs));
+ return fs->NewAppendableFile(fname, result);
+}
+
+Status Env::NewReadOnlyMemoryRegionFromFile(const string& fname,
+ ReadOnlyMemoryRegion** result) {
+ FileSystem* fs;
+ TF_RETURN_IF_ERROR(GetFileSystemForFile(fname, &fs));
+ return fs->NewReadOnlyMemoryRegionFromFile(fname, result);
+}
+
+bool Env::FileExists(const string& fname) {
+ FileSystem* fs;
+ if (!GetFileSystemForFile(fname, &fs).ok()) {
+ return false;
+ }
+ return fs->FileExists(fname);
+}
-WritableFile::~WritableFile() {}
+Status Env::GetChildren(const string& dir, std::vector<string>* result) {
+ FileSystem* fs;
+ TF_RETURN_IF_ERROR(GetFileSystemForFile(dir, &fs));
+ return fs->GetChildren(dir, result);
+}
+
+Status Env::DeleteFile(const string& fname) {
+ FileSystem* fs;
+ TF_RETURN_IF_ERROR(GetFileSystemForFile(fname, &fs));
+ return fs->DeleteFile(fname);
+}
+
+Status Env::CreateDir(const string& dirname) {
+ FileSystem* fs;
+ TF_RETURN_IF_ERROR(GetFileSystemForFile(dirname, &fs));
+ return fs->CreateDir(dirname);
+}
+
+Status Env::DeleteDir(const string& dirname) {
+ FileSystem* fs;
+ TF_RETURN_IF_ERROR(GetFileSystemForFile(dirname, &fs));
+ return fs->DeleteDir(dirname);
+}
+
+Status Env::GetFileSize(const string& fname, uint64* file_size) {
+ FileSystem* fs;
+ TF_RETURN_IF_ERROR(GetFileSystemForFile(fname, &fs));
+ return fs->GetFileSize(fname, file_size);
+}
+
+Status Env::RenameFile(const string& src, const string& target) {
+ FileSystem* src_fs;
+ FileSystem* target_fs;
+ TF_RETURN_IF_ERROR(GetFileSystemForFile(src, &src_fs));
+ TF_RETURN_IF_ERROR(GetFileSystemForFile(target, &target_fs));
+ if (src_fs != target_fs) {
+ return errors::Unimplemented("Renaming ", src, " to ", target,
+ " not implemented");
+ }
+ return src_fs->RenameFile(src, target);
+}
Thread::~Thread() {}
diff --git a/tensorflow/core/platform/env.h b/tensorflow/core/platform/env.h
index 91b22140a1..77372d1b4e 100644
--- a/tensorflow/core/platform/env.h
+++ b/tensorflow/core/platform/env.h
@@ -18,19 +18,20 @@ limitations under the License.
#include <stdint.h>
#include <string>
+#include <unordered_map>
#include <vector>
+#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/lib/core/status.h"
#include "tensorflow/core/lib/core/stringpiece.h"
+#include "tensorflow/core/platform/file_system.h"
#include "tensorflow/core/platform/macros.h"
+#include "tensorflow/core/platform/mutex.h"
#include "tensorflow/core/platform/protobuf.h"
#include "tensorflow/core/platform/types.h"
namespace tensorflow {
-class RandomAccessFile;
-class ReadOnlyMemoryRegion;
class Thread;
-class WritableFile;
struct ThreadOptions;
/// \brief An interface used by the tensorflow implementation to
@@ -55,6 +56,11 @@ class Env {
/// The result of Default() belongs to this library and must never be deleted.
static Env* Default();
+ /// \brief Returns the FileSystem object to handle operations on the file
+ /// specified by 'fname'. The FileSystem object is used as the implementation
+ /// for the file system related (non-virtual) functions that follow.
+ virtual Status GetFileSystemForFile(const string& fname, FileSystem** result);
+
/// \brief Creates a brand new random access read-only file with the
/// specified name.
@@ -64,8 +70,7 @@ class Env {
/// status.
///
/// The returned file may be concurrently accessed by multiple threads.
- virtual Status NewRandomAccessFile(const string& fname,
- RandomAccessFile** result) = 0;
+ Status NewRandomAccessFile(const string& fname, RandomAccessFile** result);
/// \brief Creates an object that writes to a new file with the specified
/// name.
@@ -76,8 +81,7 @@ class Env {
/// returns non-OK.
///
/// The returned file will only be accessed by one thread at a time.
- virtual Status NewWritableFile(const string& fname,
- WritableFile** result) = 0;
+ Status NewWritableFile(const string& fname, WritableFile** result);
/// \brief Creates an object that either appends to an existing file, or
/// writes to a new file (if the file does not exist to begin with).
@@ -87,8 +91,7 @@ class Env {
/// non-OK.
///
/// The returned file will only be accessed by one thread at a time.
- virtual Status NewAppendableFile(const string& fname,
- WritableFile** result) = 0;
+ Status NewAppendableFile(const string& fname, WritableFile** result);
/// \brief Creates a readonly region of memory with the file context.
///
@@ -97,34 +100,33 @@ class Env {
/// the caller. On failure stores nullptr in *result and returns non-OK.
///
/// The returned memory region can be accessed from many threads in parallel.
- virtual Status NewReadOnlyMemoryRegionFromFile(
- const string& fname, ReadOnlyMemoryRegion** result) = 0;
+ Status NewReadOnlyMemoryRegionFromFile(const string& fname,
+ ReadOnlyMemoryRegion** result);
/// Returns true iff the named file exists.
- virtual bool FileExists(const string& fname) = 0;
+ bool FileExists(const string& fname);
/// \brief Stores in *result the names of the children of the specified
/// directory. The names are relative to "dir".
///
/// Original contents of *results are dropped.
- virtual Status GetChildren(const string& dir,
- std::vector<string>* result) = 0;
+ Status GetChildren(const string& dir, std::vector<string>* result);
/// Deletes the named file.
- virtual Status DeleteFile(const string& fname) = 0;
+ Status DeleteFile(const string& fname);
/// Creates the specified directory.
- virtual Status CreateDir(const string& dirname) = 0;
+ Status CreateDir(const string& dirname);
/// Deletes the specified directory.
- virtual Status DeleteDir(const string& dirname) = 0;
+ Status DeleteDir(const string& dirname);
/// Stores the size of `fname` in `*file_size`.
- virtual Status GetFileSize(const string& fname, uint64* file_size) = 0;
+ Status GetFileSize(const string& fname, uint64* file_size);
/// \brief Renames file src to target. If target already exists, it will be
/// replaced.
- virtual Status RenameFile(const string& src, const string& target) = 0;
+ Status RenameFile(const string& src, const string& target);
// TODO(jeff,sanjay): Add back thread/thread-pool support if needed.
// TODO(jeff,sanjay): if needed, tighten spec so relative to epoch, or
@@ -184,68 +186,6 @@ class Env {
void operator=(const Env&);
};
-/// A file abstraction for randomly reading the contents of a file.
-class RandomAccessFile {
- public:
- RandomAccessFile() {}
- virtual ~RandomAccessFile();
-
- /// \brief Reads up to `n` bytes from the file starting at `offset`.
- ///
- /// `scratch[0..n-1]` may be written by this routine. Sets `*result`
- /// to the data that was read (including if fewer than `n` bytes were
- /// successfully read). May set `*result` to point at data in
- /// `scratch[0..n-1]`, so `scratch[0..n-1]` must be live when
- /// `*result` is used.
- ///
- /// On OK returned status: `n` bytes have been stored in `*result`.
- /// On non-OK returned status: `[0..n]` bytes have been stored in `*result`.
- ///
- /// Returns `OUT_OF_RANGE` if fewer than n bytes were stored in `*result`
- /// because of EOF.
- ///
- /// Safe for concurrent use by multiple threads.
- virtual Status Read(uint64 offset, size_t n, StringPiece* result,
- char* scratch) const = 0;
-
- private:
- /// No copying allowed
- RandomAccessFile(const RandomAccessFile&);
- void operator=(const RandomAccessFile&);
-};
-
-/// \brief A file abstraction for sequential writing.
-///
-/// The implementation must provide buffering since callers may append
-/// small fragments at a time to the file.
-class WritableFile {
- public:
- WritableFile() {}
- virtual ~WritableFile();
-
- virtual Status Append(const StringPiece& data) = 0;
- virtual Status Close() = 0;
- virtual Status Flush() = 0;
- virtual Status Sync() = 0;
-
- private:
- /// No copying allowed
- WritableFile(const WritableFile&);
- void operator=(const WritableFile&);
-};
-
-/// \brief A readonly memmapped file abstraction.
-///
-/// The implementation must guarantee that all memory is accessable when the
-/// object exists, independently from the Env that created it.
-class ReadOnlyMemoryRegion {
- public:
- ReadOnlyMemoryRegion() {}
- virtual ~ReadOnlyMemoryRegion() = default;
- virtual const void* data() = 0;
- virtual uint64 length() = 0;
-};
-
/// \brief An implementation of Env that forwards all calls to another Env.
///
/// May be useful to clients who wish to override just part of the
@@ -259,33 +199,11 @@ class EnvWrapper : public Env {
/// Returns the target to which this Env forwards all calls
Env* target() const { return target_; }
- // The following text is boilerplate that forwards all methods to target()
- Status NewRandomAccessFile(const string& f, RandomAccessFile** r) override {
- return target_->NewRandomAccessFile(f, r);
- }
- Status NewWritableFile(const string& f, WritableFile** r) override {
- return target_->NewWritableFile(f, r);
- }
- Status NewAppendableFile(const string& f, WritableFile** r) override {
- return target_->NewAppendableFile(f, r);
- }
- Status NewReadOnlyMemoryRegionFromFile(
- const string& fname, ReadOnlyMemoryRegion** result) override {
- return target_->NewReadOnlyMemoryRegionFromFile(fname, result);
- }
- bool FileExists(const string& f) override { return target_->FileExists(f); }
- Status GetChildren(const string& dir, std::vector<string>* r) override {
- return target_->GetChildren(dir, r);
- }
- Status DeleteFile(const string& f) override { return target_->DeleteFile(f); }
- Status CreateDir(const string& d) override { return target_->CreateDir(d); }
- Status DeleteDir(const string& d) override { return target_->DeleteDir(d); }
- Status GetFileSize(const string& f, uint64* s) override {
- return target_->GetFileSize(f, s);
- }
- Status RenameFile(const string& s, const string& t) override {
- return target_->RenameFile(s, t);
+ Status GetFileSystemForFile(const string& fname,
+ FileSystem** result) override {
+ return target_->GetFileSystemForFile(fname, result);
}
+
uint64 NowMicros() override { return target_->NowMicros(); }
void SleepForMicroseconds(int micros) override {
target_->SleepForMicroseconds(micros);
diff --git a/tensorflow/core/platform/env_test.cc b/tensorflow/core/platform/env_test.cc
index b863fd623a..6bf69f988c 100644
--- a/tensorflow/core/platform/env_test.cc
+++ b/tensorflow/core/platform/env_test.cc
@@ -71,4 +71,49 @@ TEST(EnvTest, FileToReadonlyMemoryRegion) {
}
}
+TEST(EnvTest, LocalFileSystem) {
+ // Test filename with file:// syntax.
+ Env* env = Env::Default();
+ const string dir = testing::TmpDir();
+ for (const int length : {0, 1, 1212, 2553, 4928, 8196, 9000, (1 << 20) - 1,
+ 1 << 20, (1 << 20) + 1}) {
+ string filename = io::JoinPath(dir, strings::StrCat("file", length));
+
+ filename = strings::StrCat("file://", filename);
+
+ // Write a file with the given length
+ const string input = CreateTestFile(env, filename, length);
+
+ // Read the file back and check equality
+ string output;
+ TF_CHECK_OK(ReadFileToString(env, filename, &output));
+ CHECK_EQ(length, output.size());
+ CHECK_EQ(input, output);
+ }
+}
+
+class InterPlanetaryFileSystem : public NullFileSystem {
+ public:
+ Status GetChildren(const string& dir, std::vector<string>* result) override {
+ std::vector<string> Planets = {"Mercury", "Venus", "Earth", "Mars",
+ "Jupiter", "Saturn", "Uranus", "Neptune"};
+ result->insert(result->end(), Planets.begin(), Planets.end());
+ return Status::OK();
+ }
+};
+
+REGISTER_FILE_SYSTEM("ipfs", InterPlanetaryFileSystem);
+
+TEST(EnvTest, IPFS) {
+ Env* env = Env::Default();
+ std::vector<string> planets;
+ TF_CHECK_OK(env->GetChildren("ipfs://solarsystem", &planets));
+ int c = 0;
+ std::vector<string> Planets = {"Mercury", "Venus", "Earth", "Mars",
+ "Jupiter", "Saturn", "Uranus", "Neptune"};
+ for (auto p : Planets) {
+ EXPECT_EQ(p, planets[c++]);
+ }
+}
+
} // namespace tensorflow
diff --git a/tensorflow/core/platform/file_system.cc b/tensorflow/core/platform/file_system.cc
new file mode 100644
index 0000000000..4f19bff50b
--- /dev/null
+++ b/tensorflow/core/platform/file_system.cc
@@ -0,0 +1,92 @@
+/* Copyright 2015 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+==============================================================================*/
+
+#include "tensorflow/core/platform/file_system.h"
+#include "tensorflow/core/lib/core/errors.h"
+#include "tensorflow/core/lib/gtl/map_util.h"
+#include "tensorflow/core/lib/gtl/stl_util.h"
+#include "tensorflow/core/lib/strings/str_util.h"
+#include "tensorflow/core/platform/protobuf.h"
+
+namespace tensorflow {
+
+FileSystem::~FileSystem() {}
+
+string FileSystem::TranslateName(const string& name) const { return name; }
+
+RandomAccessFile::~RandomAccessFile() {}
+
+WritableFile::~WritableFile() {}
+
+FileSystemRegistry::~FileSystemRegistry() {}
+
+class FileSystemRegistryImpl : public FileSystemRegistry {
+ public:
+ void Register(const string& scheme, Factory factory) override;
+ FileSystem* Lookup(const string& scheme) override;
+
+ private:
+ mutable mutex mu_;
+ mutable std::unordered_map<string, FileSystem*> registry_ GUARDED_BY(mu_);
+};
+
+FileSystemRegistry* GlobalFileSystemRegistry() {
+ static FileSystemRegistry* registry = new FileSystemRegistryImpl;
+ return registry;
+}
+
+void FileSystemRegistryImpl::Register(const string& scheme,
+ FileSystemRegistry::Factory factory) {
+ mutex_lock lock(mu_);
+ QCHECK(!gtl::FindOrNull(registry_, scheme)) << "File factory for " << scheme
+ << " already registered";
+ registry_[scheme] = factory();
+}
+
+FileSystem* FileSystemRegistryImpl::Lookup(const string& scheme) {
+ mutex_lock lock(mu_);
+ auto fs_ptr = gtl::FindOrNull(registry_, scheme);
+ if (!fs_ptr) {
+ return nullptr;
+ }
+ return *fs_ptr;
+}
+
+string GetSchemeFromURI(const string& name) {
+ auto colon_loc = name.find(":");
+ if (colon_loc != string::npos) {
+ return name.substr(0, colon_loc);
+ }
+ return "";
+}
+
+string GetNameFromURI(const string& name) {
+ string scheme = GetSchemeFromURI(name);
+ if (scheme == "") {
+ return name;
+ }
+ // Skip the 'scheme:' portion.
+ StringPiece filename{name.data() + scheme.length() + 1,
+ name.length() - scheme.length() - 1};
+ // If the URI confirmed to scheme://filename, skip the two '/'s and return
+ // filename. Otherwise return the original 'name', and leave it up to the
+ // implementations to handle the full URI.
+ if (filename[0] == '/' && filename[1] == '/') {
+ return filename.substr(2).ToString();
+ }
+ return name;
+}
+
+} // namespace tensorflow
diff --git a/tensorflow/core/platform/file_system.h b/tensorflow/core/platform/file_system.h
new file mode 100644
index 0000000000..5fbd097ab2
--- /dev/null
+++ b/tensorflow/core/platform/file_system.h
@@ -0,0 +1,243 @@
+/* Copyright 2015 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+==============================================================================*/
+
+#ifndef TENSORFLOW_CORE_PLATFORM_FILE_SYSTEM_H_
+#define TENSORFLOW_CORE_PLATFORM_FILE_SYSTEM_H_
+
+#include <stdint.h>
+#include <functional>
+#include <string>
+#include <unordered_map>
+#include <vector>
+#include "tensorflow/core/lib/core/errors.h"
+#include "tensorflow/core/lib/core/status.h"
+#include "tensorflow/core/lib/core/stringpiece.h"
+#include "tensorflow/core/platform/macros.h"
+#include "tensorflow/core/platform/mutex.h"
+#include "tensorflow/core/platform/protobuf.h"
+#include "tensorflow/core/platform/types.h"
+
+namespace tensorflow {
+
+class RandomAccessFile;
+class ReadOnlyMemoryRegion;
+class WritableFile;
+
+/// An generic interface for accessing a file system.
+class FileSystem {
+ public:
+ FileSystem() {}
+
+ virtual ~FileSystem();
+
+ /// The following functions are the implementations used by the corresponding
+ /// functions in the Env class.
+ virtual Status NewRandomAccessFile(const string& fname,
+ RandomAccessFile** result) = 0;
+
+ virtual Status NewWritableFile(const string& fname,
+ WritableFile** result) = 0;
+
+ virtual Status NewAppendableFile(const string& fname,
+ WritableFile** result) = 0;
+
+ virtual Status NewReadOnlyMemoryRegionFromFile(
+ const string& fname, ReadOnlyMemoryRegion** result) = 0;
+
+ virtual bool FileExists(const string& fname) = 0;
+
+ virtual Status GetChildren(const string& dir,
+ std::vector<string>* result) = 0;
+
+ virtual Status DeleteFile(const string& fname) = 0;
+
+ virtual Status CreateDir(const string& dirname) = 0;
+
+ virtual Status DeleteDir(const string& dirname) = 0;
+
+ virtual Status GetFileSize(const string& fname, uint64* file_size) = 0;
+
+ virtual Status RenameFile(const string& src, const string& target) = 0;
+
+ // Translate an URI to a filename usable by the FileSystem implementation. The
+ // implementation in this class returns the name as-is.
+ virtual string TranslateName(const string& name) const;
+};
+
+// Degenerate file system that provides no implementations.
+class NullFileSystem : public FileSystem {
+ public:
+ NullFileSystem() {}
+
+ ~NullFileSystem() override = default;
+
+ Status NewRandomAccessFile(const string& fname,
+ RandomAccessFile** result) override {
+ return errors::Unimplemented("NewRandomAccessFile unimplemented");
+ }
+
+ Status NewWritableFile(const string& fname, WritableFile** result) override {
+ return errors::Unimplemented("NewWritableFile unimplemented");
+ }
+
+ Status NewAppendableFile(const string& fname,
+ WritableFile** result) override {
+ return errors::Unimplemented("NewAppendableFile unimplemented");
+ }
+
+ Status NewReadOnlyMemoryRegionFromFile(
+ const string& fname, ReadOnlyMemoryRegion** result) override {
+ return errors::Unimplemented(
+ "NewReadOnlyMemoryRegionFromFile unimplemented");
+ }
+
+ bool FileExists(const string& fname) override { return false; }
+
+ Status GetChildren(const string& dir, std::vector<string>* result) override {
+ return errors::Unimplemented("GetChildren unimplemented");
+ }
+
+ Status DeleteFile(const string& fname) override {
+ return errors::Unimplemented("DeleteFile unimplemented");
+ }
+
+ Status CreateDir(const string& dirname) override {
+ return errors::Unimplemented("CreateDir unimplemented");
+ }
+
+ Status DeleteDir(const string& dirname) override {
+ return errors::Unimplemented("DeleteDir unimplemented");
+ }
+
+ Status GetFileSize(const string& fname, uint64* file_size) override {
+ return errors::Unimplemented("GetFileSize unimplemented");
+ }
+
+ Status RenameFile(const string& src, const string& target) override {
+ return errors::Unimplemented("RenameFile unimplemented");
+ }
+};
+
+/// A file abstraction for randomly reading the contents of a file.
+class RandomAccessFile {
+ public:
+ RandomAccessFile() {}
+ virtual ~RandomAccessFile();
+
+ /// \brief Reads up to `n` bytes from the file starting at `offset`.
+ ///
+ /// `scratch[0..n-1]` may be written by this routine. Sets `*result`
+ /// to the data that was read (including if fewer than `n` bytes were
+ /// successfully read). May set `*result` to point at data in
+ /// `scratch[0..n-1]`, so `scratch[0..n-1]` must be live when
+ /// `*result` is used.
+ ///
+ /// On OK returned status: `n` bytes have been stored in `*result`.
+ /// On non-OK returned status: `[0..n]` bytes have been stored in `*result`.
+ ///
+ /// Returns `OUT_OF_RANGE` if fewer than n bytes were stored in `*result`
+ /// because of EOF.
+ ///
+ /// Safe for concurrent use by multiple threads.
+ virtual Status Read(uint64 offset, size_t n, StringPiece* result,
+ char* scratch) const = 0;
+
+ private:
+ /// No copying allowed
+ RandomAccessFile(const RandomAccessFile&);
+ void operator=(const RandomAccessFile&);
+};
+
+/// \brief A file abstraction for sequential writing.
+///
+/// The implementation must provide buffering since callers may append
+/// small fragments at a time to the file.
+class WritableFile {
+ public:
+ WritableFile() {}
+ virtual ~WritableFile();
+
+ virtual Status Append(const StringPiece& data) = 0;
+ virtual Status Close() = 0;
+ virtual Status Flush() = 0;
+ virtual Status Sync() = 0;
+
+ private:
+ /// No copying allowed
+ WritableFile(const WritableFile&);
+ void operator=(const WritableFile&);
+};
+
+/// \brief A readonly memmapped file abstraction.
+///
+/// The implementation must guarantee that all memory is accessable when the
+/// object exists, independently from the Env that created it.
+class ReadOnlyMemoryRegion {
+ public:
+ ReadOnlyMemoryRegion() {}
+ virtual ~ReadOnlyMemoryRegion() = default;
+ virtual const void* data() = 0;
+ virtual uint64 length() = 0;
+};
+
+/// \brief A registry for file system implementations.
+///
+/// Filenames are specified as an URI, which is of the form
+/// [scheme://]<filename>.
+/// File system implementations are registered using the REGISTER_FILE_SYSTEM
+/// macro, providing the 'scheme' as the key.
+class FileSystemRegistry {
+ public:
+ typedef std::function<FileSystem*()> Factory;
+
+ virtual ~FileSystemRegistry();
+ virtual void Register(const string& scheme, Factory factory) = 0;
+ virtual FileSystem* Lookup(const string& scheme) = 0;
+};
+
+FileSystemRegistry* GlobalFileSystemRegistry();
+
+namespace register_file_system {
+
+template <typename Factory>
+struct Register {
+ Register(const string& scheme) {
+ ::tensorflow::GlobalFileSystemRegistry()->Register(
+ scheme, []() -> FileSystem* { return new Factory; });
+ }
+};
+
+} // namespace register_file_system
+
+// Given URI of the form [scheme://]<filename>, return 'scheme'.
+string GetSchemeFromURI(const string& name);
+
+// Given URI of the form [scheme://]<filename>, return 'filename'.
+string GetNameFromURI(const string& name);
+
+} // namespace tensorflow
+
+// Register a FileSystem implementation for a scheme. Files with names that have
+// "scheme://" prefixes are routed to use this implementation.
+#define REGISTER_FILE_SYSTEM(scheme, factory) \
+ REGISTER_FILE_SYSTEM_UNIQ_HELPER(__COUNTER__, scheme, factory)
+#define REGISTER_FILE_SYSTEM_UNIQ_HELPER(ctr, scheme, factory) \
+ REGISTER_FILE_SYSTEM_UNIQ(ctr, scheme, factory)
+#define REGISTER_FILE_SYSTEM_UNIQ(ctr, scheme, factory) \
+ static ::tensorflow::register_file_system::Register<factory> \
+ register_ff##ctr TF_ATTRIBUTE_UNUSED = \
+ ::tensorflow::register_file_system::Register<factory>(scheme)
+
+#endif // TENSORFLOW_CORE_PLATFORM_FILE_SYSTEM_H_
diff --git a/tensorflow/core/platform/posix/env.cc b/tensorflow/core/platform/posix/env.cc
index 14ee5c1266..a0ec82466f 100644
--- a/tensorflow/core/platform/posix/env.cc
+++ b/tensorflow/core/platform/posix/env.cc
@@ -31,245 +31,12 @@ limitations under the License.
#include "tensorflow/core/platform/env.h"
#include "tensorflow/core/platform/load_library.h"
#include "tensorflow/core/platform/logging.h"
+#include "tensorflow/core/platform/posix/posix_file_system.h"
namespace tensorflow {
namespace {
-error::Code ErrnoToCode(int err_number) {
- error::Code code;
- switch (err_number) {
- case 0:
- code = error::OK;
- break;
- case EINVAL: // Invalid argument
- case ENAMETOOLONG: // Filename too long
- case E2BIG: // Argument list too long
- case EDESTADDRREQ: // Destination address required
- case EDOM: // Mathematics argument out of domain of function
- case EFAULT: // Bad address
- case EILSEQ: // Illegal byte sequence
- case ENOPROTOOPT: // Protocol not available
- case ENOSTR: // Not a STREAM
- case ENOTSOCK: // Not a socket
- case ENOTTY: // Inappropriate I/O control operation
- case EPROTOTYPE: // Protocol wrong type for socket
- case ESPIPE: // Invalid seek
- code = error::INVALID_ARGUMENT;
- break;
- case ETIMEDOUT: // Connection timed out
- case ETIME: // Timer expired
- code = error::DEADLINE_EXCEEDED;
- break;
- case ENODEV: // No such device
- case ENOENT: // No such file or directory
- case ENXIO: // No such device or address
- case ESRCH: // No such process
- code = error::NOT_FOUND;
- break;
- case EEXIST: // File exists
- case EADDRNOTAVAIL: // Address not available
- case EALREADY: // Connection already in progress
- code = error::ALREADY_EXISTS;
- break;
- case EPERM: // Operation not permitted
- case EACCES: // Permission denied
- case EROFS: // Read only file system
- code = error::PERMISSION_DENIED;
- break;
- case ENOTEMPTY: // Directory not empty
- case EISDIR: // Is a directory
- case ENOTDIR: // Not a directory
- case EADDRINUSE: // Address already in use
- case EBADF: // Invalid file descriptor
- case EBUSY: // Device or resource busy
- case ECHILD: // No child processes
- case EISCONN: // Socket is connected
- case ENOTBLK: // Block device required
- case ENOTCONN: // The socket is not connected
- case EPIPE: // Broken pipe
- case ESHUTDOWN: // Cannot send after transport endpoint shutdown
- case ETXTBSY: // Text file busy
- code = error::FAILED_PRECONDITION;
- break;
- case ENOSPC: // No space left on device
- case EDQUOT: // Disk quota exceeded
- case EMFILE: // Too many open files
- case EMLINK: // Too many links
- case ENFILE: // Too many open files in system
- case ENOBUFS: // No buffer space available
- case ENODATA: // No message is available on the STREAM read queue
- case ENOMEM: // Not enough space
- case ENOSR: // No STREAM resources
- case EUSERS: // Too many users
- code = error::RESOURCE_EXHAUSTED;
- break;
- case EFBIG: // File too large
- case EOVERFLOW: // Value too large to be stored in data type
- case ERANGE: // Result too large
- code = error::OUT_OF_RANGE;
- break;
- case ENOSYS: // Function not implemented
- case ENOTSUP: // Operation not supported
- case EAFNOSUPPORT: // Address family not supported
- case EPFNOSUPPORT: // Protocol family not supported
- case EPROTONOSUPPORT: // Protocol not supported
- case ESOCKTNOSUPPORT: // Socket type not supported
- case EXDEV: // Improper link
- code = error::UNIMPLEMENTED;
- break;
- case EAGAIN: // Resource temporarily unavailable
- case ECONNREFUSED: // Connection refused
- case ECONNABORTED: // Connection aborted
- case ECONNRESET: // Connection reset
- case EINTR: // Interrupted function call
- case EHOSTDOWN: // Host is down
- case EHOSTUNREACH: // Host is unreachable
- case ENETDOWN: // Network is down
- case ENETRESET: // Connection aborted by network
- case ENETUNREACH: // Network unreachable
- case ENOLCK: // No locks available
- case ENOLINK: // Link has been severed
-#if !defined(__APPLE__)
- case ENONET: // Machine is not on the network
-#endif
- code = error::UNAVAILABLE;
- break;
- case EDEADLK: // Resource deadlock avoided
- case ESTALE: // Stale file handle
- code = error::ABORTED;
- break;
- case ECANCELED: // Operation cancelled
- code = error::CANCELLED;
- break;
- // NOTE: If you get any of the following (especially in a
- // reproducible way) and can propose a better mapping,
- // please email the owners about updating this mapping.
- case EBADMSG: // Bad message
- case EIDRM: // Identifier removed
- case EINPROGRESS: // Operation in progress
- case EIO: // I/O error
- case ELOOP: // Too many levels of symbolic links
- case ENOEXEC: // Exec format error
- case ENOMSG: // No message of the desired type
- case EPROTO: // Protocol error
- case EREMOTE: // Object is remote
- code = error::UNKNOWN;
- break;
- default: {
- code = error::UNKNOWN;
- break;
- }
- }
- return code;
-}
-
-static Status IOError(const string& context, int err_number) {
- auto code = ErrnoToCode(err_number);
- if (code == error::UNKNOWN) {
- return Status(ErrnoToCode(err_number),
- context + "; " + strerror(err_number));
- } else {
- return Status(ErrnoToCode(err_number), context);
- }
-}
-
-// pread() based random-access
-class PosixRandomAccessFile : public RandomAccessFile {
- private:
- string filename_;
- int fd_;
-
- public:
- PosixRandomAccessFile(const string& fname, int fd)
- : filename_(fname), fd_(fd) {}
- ~PosixRandomAccessFile() override { close(fd_); }
-
- Status Read(uint64 offset, size_t n, StringPiece* result,
- char* scratch) const override {
- Status s;
- char* dst = scratch;
- while (n > 0 && s.ok()) {
- ssize_t r = pread(fd_, dst, n, static_cast<off_t>(offset));
- if (r > 0) {
- dst += r;
- n -= r;
- offset += r;
- } else if (r == 0) {
- s = Status(error::OUT_OF_RANGE, "Read less bytes than requested");
- } else if (errno == EINTR || errno == EAGAIN) {
- // Retry
- } else {
- s = IOError(filename_, errno);
- }
- }
- *result = StringPiece(scratch, dst - scratch);
- return s;
- }
-};
-
-class PosixWritableFile : public WritableFile {
- private:
- string filename_;
- FILE* file_;
-
- public:
- PosixWritableFile(const string& fname, FILE* f)
- : filename_(fname), file_(f) {}
-
- ~PosixWritableFile() override {
- if (file_ != NULL) {
- // Ignoring any potential errors
- fclose(file_);
- }
- }
-
- Status Append(const StringPiece& data) override {
- size_t r = fwrite(data.data(), 1, data.size(), file_);
- if (r != data.size()) {
- return IOError(filename_, errno);
- }
- return Status::OK();
- }
-
- Status Close() override {
- Status result;
- if (fclose(file_) != 0) {
- result = IOError(filename_, errno);
- }
- file_ = NULL;
- return result;
- }
-
- Status Flush() override {
- if (fflush(file_) != 0) {
- return IOError(filename_, errno);
- }
- return Status::OK();
- }
-
- Status Sync() override {
- Status s;
- if (fflush(file_) != 0) {
- s = IOError(filename_, errno);
- }
- return s;
- }
-};
-
-class PosixReadOnlyMemoryRegion : public ReadOnlyMemoryRegion {
- public:
- PosixReadOnlyMemoryRegion(const void* address, uint64 length)
- : address_(address), length_(length) {}
- ~PosixReadOnlyMemoryRegion() { munmap(const_cast<void*>(address_), length_); }
- const void* data() override { return address_; }
- uint64 length() override { return length_; }
-
- private:
- const void* const address_;
- const uint64 length_;
-};
-
class StdThread : public Thread {
public:
// name and thread_options are both ignored.
@@ -288,131 +55,6 @@ class PosixEnv : public Env {
~PosixEnv() override { LOG(FATAL) << "Env::Default() must not be destroyed"; }
- Status NewRandomAccessFile(const string& fname,
- RandomAccessFile** result) override {
- *result = NULL;
- Status s;
- int fd = open(fname.c_str(), O_RDONLY);
- if (fd < 0) {
- s = IOError(fname, errno);
- } else {
- *result = new PosixRandomAccessFile(fname, fd);
- }
- return s;
- }
-
- Status NewWritableFile(const string& fname, WritableFile** result) override {
- Status s;
- FILE* f = fopen(fname.c_str(), "w");
- if (f == NULL) {
- *result = NULL;
- s = IOError(fname, errno);
- } else {
- *result = new PosixWritableFile(fname, f);
- }
- return s;
- }
-
- Status NewAppendableFile(const string& fname,
- WritableFile** result) override {
- Status s;
- FILE* f = fopen(fname.c_str(), "a");
- if (f == NULL) {
- *result = NULL;
- s = IOError(fname, errno);
- } else {
- *result = new PosixWritableFile(fname, f);
- }
- return s;
- }
-
- Status NewReadOnlyMemoryRegionFromFile(
- const string& fname, ReadOnlyMemoryRegion** result) override {
- *result = nullptr;
- Status s = Status::OK();
- int fd = open(fname.c_str(), O_RDONLY);
- if (fd < 0) {
- s = IOError(fname, errno);
- } else {
- struct stat st;
- ::fstat(fd, &st);
- const void* address =
- mmap(nullptr, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
- if (address == MAP_FAILED) {
- s = IOError(fname, errno);
- } else {
- *result = new PosixReadOnlyMemoryRegion(address, st.st_size);
- }
- close(fd);
- }
- return s;
- }
-
- bool FileExists(const string& fname) override {
- return access(fname.c_str(), F_OK) == 0;
- }
-
- Status GetChildren(const string& dir, std::vector<string>* result) override {
- result->clear();
- DIR* d = opendir(dir.c_str());
- if (d == NULL) {
- return IOError(dir, errno);
- }
- struct dirent* entry;
- while ((entry = readdir(d)) != NULL) {
- StringPiece basename = entry->d_name;
- if ((basename != ".") && (basename != "..")) {
- result->push_back(entry->d_name);
- }
- }
- closedir(d);
- return Status::OK();
- }
-
- Status DeleteFile(const string& fname) override {
- Status result;
- if (unlink(fname.c_str()) != 0) {
- result = IOError(fname, errno);
- }
- return result;
- }
-
- Status CreateDir(const string& name) override {
- Status result;
- if (mkdir(name.c_str(), 0755) != 0) {
- result = IOError(name, errno);
- }
- return result;
- }
-
- Status DeleteDir(const string& name) override {
- Status result;
- if (rmdir(name.c_str()) != 0) {
- result = IOError(name, errno);
- }
- return result;
- }
-
- Status GetFileSize(const string& fname, uint64* size) override {
- Status s;
- struct stat sbuf;
- if (stat(fname.c_str(), &sbuf) != 0) {
- *size = 0;
- s = IOError(fname, errno);
- } else {
- *size = sbuf.st_size;
- }
- return s;
- }
-
- Status RenameFile(const string& src, const string& target) override {
- Status result;
- if (rename(src.c_str(), target.c_str()) != 0) {
- result = IOError(src, errno);
- }
- return result;
- }
-
uint64 NowMicros() override {
struct timeval tv;
gettimeofday(&tv, NULL);
@@ -458,6 +100,8 @@ class PosixEnv : public Env {
} // namespace
#if defined(PLATFORM_POSIX) || defined(__ANDROID__)
+REGISTER_FILE_SYSTEM("", PosixFileSystem);
+REGISTER_FILE_SYSTEM("file", LocalPosixFileSystem);
Env* Env::Default() {
static Env* default_env = new PosixEnv;
return default_env;
diff --git a/tensorflow/core/platform/posix/posix_file_system.cc b/tensorflow/core/platform/posix/posix_file_system.cc
new file mode 100644
index 0000000000..17cb668d35
--- /dev/null
+++ b/tensorflow/core/platform/posix/posix_file_system.cc
@@ -0,0 +1,404 @@
+/* Copyright 2015 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+==============================================================================*/
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "tensorflow/core/lib/core/error_codes.pb.h"
+#include "tensorflow/core/lib/strings/strcat.h"
+#include "tensorflow/core/platform/env.h"
+#include "tensorflow/core/platform/logging.h"
+#include "tensorflow/core/platform/posix/posix_file_system.h"
+
+namespace tensorflow {
+
+namespace {
+
+error::Code ErrnoToCode(int err_number) {
+ error::Code code;
+ switch (err_number) {
+ case 0:
+ code = error::OK;
+ break;
+ case EINVAL: // Invalid argument
+ case ENAMETOOLONG: // Filename too long
+ case E2BIG: // Argument list too long
+ case EDESTADDRREQ: // Destination address required
+ case EDOM: // Mathematics argument out of domain of function
+ case EFAULT: // Bad address
+ case EILSEQ: // Illegal byte sequence
+ case ENOPROTOOPT: // Protocol not available
+ case ENOSTR: // Not a STREAM
+ case ENOTSOCK: // Not a socket
+ case ENOTTY: // Inappropriate I/O control operation
+ case EPROTOTYPE: // Protocol wrong type for socket
+ case ESPIPE: // Invalid seek
+ code = error::INVALID_ARGUMENT;
+ break;
+ case ETIMEDOUT: // Connection timed out
+ case ETIME: // Timer expired
+ code = error::DEADLINE_EXCEEDED;
+ break;
+ case ENODEV: // No such device
+ case ENOENT: // No such file or directory
+ case ENXIO: // No such device or address
+ case ESRCH: // No such process
+ code = error::NOT_FOUND;
+ break;
+ case EEXIST: // File exists
+ case EADDRNOTAVAIL: // Address not available
+ case EALREADY: // Connection already in progress
+ code = error::ALREADY_EXISTS;
+ break;
+ case EPERM: // Operation not permitted
+ case EACCES: // Permission denied
+ case EROFS: // Read only file system
+ code = error::PERMISSION_DENIED;
+ break;
+ case ENOTEMPTY: // Directory not empty
+ case EISDIR: // Is a directory
+ case ENOTDIR: // Not a directory
+ case EADDRINUSE: // Address already in use
+ case EBADF: // Invalid file descriptor
+ case EBUSY: // Device or resource busy
+ case ECHILD: // No child processes
+ case EISCONN: // Socket is connected
+ case ENOTBLK: // Block device required
+ case ENOTCONN: // The socket is not connected
+ case EPIPE: // Broken pipe
+ case ESHUTDOWN: // Cannot send after transport endpoint shutdown
+ case ETXTBSY: // Text file busy
+ code = error::FAILED_PRECONDITION;
+ break;
+ case ENOSPC: // No space left on device
+ case EDQUOT: // Disk quota exceeded
+ case EMFILE: // Too many open files
+ case EMLINK: // Too many links
+ case ENFILE: // Too many open files in system
+ case ENOBUFS: // No buffer space available
+ case ENODATA: // No message is available on the STREAM read queue
+ case ENOMEM: // Not enough space
+ case ENOSR: // No STREAM resources
+ case EUSERS: // Too many users
+ code = error::RESOURCE_EXHAUSTED;
+ break;
+ case EFBIG: // File too large
+ case EOVERFLOW: // Value too large to be stored in data type
+ case ERANGE: // Result too large
+ code = error::OUT_OF_RANGE;
+ break;
+ case ENOSYS: // Function not implemented
+ case ENOTSUP: // Operation not supported
+ case EAFNOSUPPORT: // Address family not supported
+ case EPFNOSUPPORT: // Protocol family not supported
+ case EPROTONOSUPPORT: // Protocol not supported
+ case ESOCKTNOSUPPORT: // Socket type not supported
+ case EXDEV: // Improper link
+ code = error::UNIMPLEMENTED;
+ break;
+ case EAGAIN: // Resource temporarily unavailable
+ case ECONNREFUSED: // Connection refused
+ case ECONNABORTED: // Connection aborted
+ case ECONNRESET: // Connection reset
+ case EINTR: // Interrupted function call
+ case EHOSTDOWN: // Host is down
+ case EHOSTUNREACH: // Host is unreachable
+ case ENETDOWN: // Network is down
+ case ENETRESET: // Connection aborted by network
+ case ENETUNREACH: // Network unreachable
+ case ENOLCK: // No locks available
+ case ENOLINK: // Link has been severed
+#if !defined(__APPLE__)
+ case ENONET: // Machine is not on the network
+#endif
+ code = error::UNAVAILABLE;
+ break;
+ case EDEADLK: // Resource deadlock avoided
+ case ESTALE: // Stale file handle
+ code = error::ABORTED;
+ break;
+ case ECANCELED: // Operation cancelled
+ code = error::CANCELLED;
+ break;
+ // NOTE: If you get any of the following (especially in a
+ // reproducible way) and can propose a better mapping,
+ // please email the owners about updating this mapping.
+ case EBADMSG: // Bad message
+ case EIDRM: // Identifier removed
+ case EINPROGRESS: // Operation in progress
+ case EIO: // I/O error
+ case ELOOP: // Too many levels of symbolic links
+ case ENOEXEC: // Exec format error
+ case ENOMSG: // No message of the desired type
+ case EPROTO: // Protocol error
+ case EREMOTE: // Object is remote
+ code = error::UNKNOWN;
+ break;
+ default: {
+ code = error::UNKNOWN;
+ break;
+ }
+ }
+ return code;
+}
+
+// pread() based random-access
+class PosixRandomAccessFile : public RandomAccessFile {
+ private:
+ string filename_;
+ int fd_;
+
+ public:
+ PosixRandomAccessFile(const string& fname, int fd)
+ : filename_(fname), fd_(fd) {}
+ ~PosixRandomAccessFile() override { close(fd_); }
+
+ Status Read(uint64 offset, size_t n, StringPiece* result,
+ char* scratch) const override {
+ Status s;
+ char* dst = scratch;
+ while (n > 0 && s.ok()) {
+ ssize_t r = pread(fd_, dst, n, static_cast<off_t>(offset));
+ if (r > 0) {
+ dst += r;
+ n -= r;
+ offset += r;
+ } else if (r == 0) {
+ s = Status(error::OUT_OF_RANGE, "Read less bytes than requested");
+ } else if (errno == EINTR || errno == EAGAIN) {
+ // Retry
+ } else {
+ s = IOError(filename_, errno);
+ }
+ }
+ *result = StringPiece(scratch, dst - scratch);
+ return s;
+ }
+};
+
+class PosixWritableFile : public WritableFile {
+ private:
+ string filename_;
+ FILE* file_;
+
+ public:
+ PosixWritableFile(const string& fname, FILE* f)
+ : filename_(fname), file_(f) {}
+
+ ~PosixWritableFile() override {
+ if (file_ != NULL) {
+ // Ignoring any potential errors
+ fclose(file_);
+ }
+ }
+
+ Status Append(const StringPiece& data) override {
+ size_t r = fwrite(data.data(), 1, data.size(), file_);
+ if (r != data.size()) {
+ return IOError(filename_, errno);
+ }
+ return Status::OK();
+ }
+
+ Status Close() override {
+ Status result;
+ if (fclose(file_) != 0) {
+ result = IOError(filename_, errno);
+ }
+ file_ = NULL;
+ return result;
+ }
+
+ Status Flush() override {
+ if (fflush(file_) != 0) {
+ return IOError(filename_, errno);
+ }
+ return Status::OK();
+ }
+
+ Status Sync() override {
+ Status s;
+ if (fflush(file_) != 0) {
+ s = IOError(filename_, errno);
+ }
+ return s;
+ }
+};
+
+class PosixReadOnlyMemoryRegion : public ReadOnlyMemoryRegion {
+ public:
+ PosixReadOnlyMemoryRegion(const void* address, uint64 length)
+ : address_(address), length_(length) {}
+ ~PosixReadOnlyMemoryRegion() { munmap(const_cast<void*>(address_), length_); }
+ const void* data() override { return address_; }
+ uint64 length() override { return length_; }
+
+ private:
+ const void* const address_;
+ const uint64 length_;
+};
+
+} // namespace
+
+Status PosixFileSystem::NewRandomAccessFile(const string& fname,
+ RandomAccessFile** result) {
+ string translated_fname = TranslateName(fname);
+ *result = NULL;
+ Status s;
+ int fd = open(translated_fname.c_str(), O_RDONLY);
+ if (fd < 0) {
+ s = IOError(fname, errno);
+ } else {
+ *result = new PosixRandomAccessFile(translated_fname, fd);
+ }
+ return s;
+}
+
+Status PosixFileSystem::NewWritableFile(const string& fname,
+ WritableFile** result) {
+ string translated_fname = TranslateName(fname);
+ Status s;
+ FILE* f = fopen(translated_fname.c_str(), "w");
+ if (f == NULL) {
+ *result = NULL;
+ s = IOError(fname, errno);
+ } else {
+ *result = new PosixWritableFile(translated_fname, f);
+ }
+ return s;
+}
+
+Status PosixFileSystem::NewAppendableFile(const string& fname,
+ WritableFile** result) {
+ string translated_fname = TranslateName(fname);
+ Status s;
+ FILE* f = fopen(translated_fname.c_str(), "a");
+ if (f == NULL) {
+ *result = NULL;
+ s = IOError(fname, errno);
+ } else {
+ *result = new PosixWritableFile(translated_fname, f);
+ }
+ return s;
+}
+
+Status PosixFileSystem::NewReadOnlyMemoryRegionFromFile(
+ const string& fname, ReadOnlyMemoryRegion** result) {
+ string translated_fname = TranslateName(fname);
+ *result = nullptr;
+ Status s = Status::OK();
+ int fd = open(translated_fname.c_str(), O_RDONLY);
+ if (fd < 0) {
+ s = IOError(fname, errno);
+ } else {
+ struct stat st;
+ ::fstat(fd, &st);
+ const void* address =
+ mmap(nullptr, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (address == MAP_FAILED) {
+ s = IOError(fname, errno);
+ } else {
+ *result = new PosixReadOnlyMemoryRegion(address, st.st_size);
+ }
+ close(fd);
+ }
+ return s;
+}
+
+bool PosixFileSystem::FileExists(const string& fname) {
+ return access(TranslateName(fname).c_str(), F_OK) == 0;
+}
+
+Status PosixFileSystem::GetChildren(const string& dir,
+ std::vector<string>* result) {
+ string translated_dir = TranslateName(dir);
+ result->clear();
+ DIR* d = opendir(translated_dir.c_str());
+ if (d == NULL) {
+ return IOError(dir, errno);
+ }
+ struct dirent* entry;
+ while ((entry = readdir(d)) != NULL) {
+ StringPiece basename = entry->d_name;
+ if ((basename != ".") && (basename != "..")) {
+ result->push_back(entry->d_name);
+ }
+ }
+ closedir(d);
+ return Status::OK();
+}
+
+Status PosixFileSystem::DeleteFile(const string& fname) {
+ Status result;
+ if (unlink(TranslateName(fname).c_str()) != 0) {
+ result = IOError(fname, errno);
+ }
+ return result;
+}
+
+Status PosixFileSystem::CreateDir(const string& name) {
+ Status result;
+ if (mkdir(TranslateName(name).c_str(), 0755) != 0) {
+ result = IOError(name, errno);
+ }
+ return result;
+}
+
+Status PosixFileSystem::DeleteDir(const string& name) {
+ Status result;
+ if (rmdir(TranslateName(name).c_str()) != 0) {
+ result = IOError(name, errno);
+ }
+ return result;
+}
+
+Status PosixFileSystem::GetFileSize(const string& fname, uint64* size) {
+ Status s;
+ struct stat sbuf;
+ if (stat(TranslateName(fname).c_str(), &sbuf) != 0) {
+ *size = 0;
+ s = IOError(fname, errno);
+ } else {
+ *size = sbuf.st_size;
+ }
+ return s;
+}
+
+Status PosixFileSystem::RenameFile(const string& src, const string& target) {
+ Status result;
+ if (rename(TranslateName(src).c_str(), TranslateName(target).c_str()) != 0) {
+ result = IOError(src, errno);
+ }
+ return result;
+}
+
+Status IOError(const string& context, int err_number) {
+ auto code = ErrnoToCode(err_number);
+ if (code == error::UNKNOWN) {
+ return Status(code, strings::StrCat(context, "; ", strerror(err_number)));
+ } else {
+ return Status(code, context);
+ }
+}
+
+} // namespace tensorflow
diff --git a/tensorflow/core/platform/posix/posix_file_system.h b/tensorflow/core/platform/posix/posix_file_system.h
new file mode 100644
index 0000000000..3e213a119f
--- /dev/null
+++ b/tensorflow/core/platform/posix/posix_file_system.h
@@ -0,0 +1,65 @@
+/* Copyright 2015 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+==============================================================================*/
+
+#ifndef TENSORFLOW_CORE_PLATFORM_POSIX_POSIX_FILE_SYSTEM_H_
+#define TENSORFLOW_CORE_PLATFORM_POSIX_POSIX_FILE_SYSTEM_H_
+
+#include "tensorflow/core/platform/env.h"
+
+namespace tensorflow {
+
+class PosixFileSystem : public FileSystem {
+ public:
+ PosixFileSystem() {}
+
+ ~PosixFileSystem() {}
+
+ Status NewRandomAccessFile(const string& fname,
+ RandomAccessFile** result) override;
+
+ Status NewWritableFile(const string& fname, WritableFile** result) override;
+
+ Status NewAppendableFile(const string& fname, WritableFile** result) override;
+
+ Status NewReadOnlyMemoryRegionFromFile(
+ const string& fname, ReadOnlyMemoryRegion** result) override;
+
+ bool FileExists(const string& fname) override;
+
+ Status GetChildren(const string& dir, std::vector<string>* result) override;
+
+ Status DeleteFile(const string& fname) override;
+
+ Status CreateDir(const string& name) override;
+
+ Status DeleteDir(const string& name) override;
+
+ Status GetFileSize(const string& fname, uint64* size) override;
+
+ Status RenameFile(const string& src, const string& target) override;
+};
+
+Status IOError(const string& context, int err_number);
+
+class LocalPosixFileSystem : public PosixFileSystem {
+ public:
+ string TranslateName(const string& name) const override {
+ return GetNameFromURI(name);
+ }
+};
+
+} // namespace tensorflow
+
+#endif // TENSORFLOW_CORE_PLATFORM_POSIX_POSIX_FILE_SYSTEM_H_
diff --git a/tensorflow/python/BUILD b/tensorflow/python/BUILD
index 16987d373f..5353ba3515 100644
--- a/tensorflow/python/BUILD
+++ b/tensorflow/python/BUILD
@@ -120,6 +120,36 @@ cc_library(
],
)
+cc_binary(
+ name = "framework/test_file_system.so",
+ srcs = ["framework/test_file_system.cc"],
+ linkopts = select({
+ "//conditions:default": [
+ "-Wl,-Bsymbolic",
+ "-lm",
+ ],
+ "//tensorflow:darwin": [],
+ }),
+ linkshared = 1,
+ deps = [
+ "//google/protobuf",
+ "//tensorflow/core:framework_headers_lib",
+ ],
+)
+
+py_test(
+ name = "file_system_test",
+ size = "small",
+ srcs = ["framework/file_system_test.py"],
+ data = [":framework/test_file_system.so"],
+ main = "framework/file_system_test.py",
+ srcs_version = "PY2AND3",
+ deps = [
+ ":framework_test_lib",
+ "//tensorflow:tensorflow_py",
+ ],
+)
+
py_test(
name = "pywrap_status_test",
size = "small",
diff --git a/tensorflow/python/framework/file_system_test.py b/tensorflow/python/framework/file_system_test.py
new file mode 100644
index 0000000000..b52a97cd72
--- /dev/null
+++ b/tensorflow/python/framework/file_system_test.py
@@ -0,0 +1,47 @@
+# Copyright 2015 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# =============================================================================
+"""Tests for functions."""
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import os
+
+import tensorflow as tf
+
+from tensorflow.python.util import compat
+
+
+class FileSystemTest(tf.test.TestCase):
+
+ def setUp(self):
+ file_system_library = os.path.join(tf.resource_loader.get_data_files_path(),
+ "test_file_system.so")
+ tf.load_file_system_library(file_system_library)
+
+ def testBasic(self):
+ with self.test_session() as sess:
+ reader = tf.WholeFileReader("test_reader")
+ queue = tf.FIFOQueue(99, [tf.string], shapes=())
+ queue.enqueue_many([["test://foo"]]).run()
+ queue.close().run()
+ key, value = sess.run(reader.read(queue))
+ self.assertEqual(key, compat.as_bytes("test://foo"))
+ self.assertEqual(value, compat.as_bytes("AAAAAAAAAA"))
+
+
+if __name__ == "__main__":
+ tf.test.main()
diff --git a/tensorflow/python/framework/framework_lib.py b/tensorflow/python/framework/framework_lib.py
index 736c30b617..f74749ad57 100644
--- a/tensorflow/python/framework/framework_lib.py
+++ b/tensorflow/python/framework/framework_lib.py
@@ -37,6 +37,7 @@
@@get_default_graph
@@reset_default_graph
@@import_graph_def
+@@load_file_system_library
@@load_op_library
## Graph collections
diff --git a/tensorflow/python/framework/load_library.py b/tensorflow/python/framework/load_library.py
index a55942675d..591c2f500c 100644
--- a/tensorflow/python/framework/load_library.py
+++ b/tensorflow/python/framework/load_library.py
@@ -92,3 +92,45 @@ def load_op_library(library_filename):
with _OP_LIBRARY_MAP_LOCK:
_OP_LIBRARY_MAP[library_filename] = module
return module
+
+
+_FILE_SYSTEM_LIBRARY_MAP = {}
+_FILE_SYSTEM_LIBRARY_MAP_LOCK = threading.Lock()
+
+
+def load_file_system_library(library_filename):
+ """Loads a TensorFlow plugin, containing file system implementation.
+
+ Pass `library_filename` to a platform-specific mechanism for dynamically
+ loading a library. The rules for determining the exact location of the
+ library are platform-specific and are not documented here.
+
+ Args:
+ library_filename: Path to the plugin.
+ Relative or absolute filesystem path to a dynamic library file.
+
+ Returns:
+ None.
+
+ Raises:
+ RuntimeError: when unable to load the library.
+ """
+ status = py_tf.TF_NewStatus()
+ lib_handle = py_tf.TF_LoadLibrary(library_filename, status)
+ try:
+ error_code = py_tf.TF_GetCode(status)
+ if error_code != 0:
+ error_msg = compat.as_text(py_tf.TF_Message(status))
+ with _FILE_SYSTEM_LIBRARY_MAP_LOCK:
+ if (error_code == error_codes_pb2.ALREADY_EXISTS and
+ 'has already been loaded' in error_msg and
+ library_filename in _FILE_SYSTEM_LIBRARY_MAP):
+ return
+ # pylint: disable=protected-access
+ raise errors._make_specific_exception(None, None, error_msg, error_code)
+ # pylint: enable=protected-access
+ finally:
+ py_tf.TF_DeleteStatus(status)
+
+ with _FILE_SYSTEM_LIBRARY_MAP_LOCK:
+ _FILE_SYSTEM_LIBRARY_MAP[library_filename] = lib_handle
diff --git a/tensorflow/python/framework/test_file_system.cc b/tensorflow/python/framework/test_file_system.cc
new file mode 100644
index 0000000000..cc0adc0f85
--- /dev/null
+++ b/tensorflow/python/framework/test_file_system.cc
@@ -0,0 +1,48 @@
+/* Copyright 2015 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+==============================================================================*/
+
+#include "tensorflow/core/platform/file_system.h"
+
+namespace tensorflow {
+
+class TestRandomAccessFile : public RandomAccessFile {
+ // The filecontents is all A's
+ Status Read(uint64 offset, size_t n, StringPiece* result,
+ char* scratch) const override {
+ for (int i = 0; i < n; ++i) {
+ scratch[i] = 'A';
+ }
+ *result = StringPiece(scratch, n);
+ return Status::OK();
+ }
+};
+
+class TestFileSystem : public NullFileSystem {
+ public:
+ Status NewRandomAccessFile(const string& fname,
+ RandomAccessFile** result) override {
+ *result = new TestRandomAccessFile;
+ return Status::OK();
+ }
+ // Always return size of 10
+ Status GetFileSize(const string& fname, uint64* file_size) override {
+ *file_size = 10;
+ return Status::OK();
+ }
+};
+
+REGISTER_FILE_SYSTEM("test", TestFileSystem);
+
+} // namespace tensorflow
diff --git a/tensorflow/tools/ci_build/builds/test_installation.sh b/tensorflow/tools/ci_build/builds/test_installation.sh
index 2b87ac3530..95d236ae0d 100755
--- a/tensorflow/tools/ci_build/builds/test_installation.sh
+++ b/tensorflow/tools/ci_build/builds/test_installation.sh
@@ -68,11 +68,14 @@
# depends on compare_test_pb2 defined outside Python
# tensorflow/python/framework/device_test.py:
# depends on CheckValid() and ToString(), both defined externally
+# tensorflow/python/framework/file_system_test.py:
+# depends on having the .so which is not shipped in the pip package.
#
PY_TEST_BLACKLIST="${PY_TEST_BLACKLIST}:"\
"tensorflow/python/framework/ops_test.py:"\
"tensorflow/python/util/protobuf/compare_test.py:"\
-"tensorflow/python/framework/device_test.py"
+"tensorflow/python/framework/device_test.py:"\
+"tensorflow/python/framework/file_system_test.py"
# Test blacklist: GPU-only
PY_TEST_GPU_BLACKLIST="${PY_TEST_GPU_BLACKLIST}:"\