1 /* Copyright 2016 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/tools/android/inference_interface/asset_manager_filesystem.h"
17 
18 #include <unistd.h>
19 
20 #include "tensorflow/core/lib/strings/str_util.h"
21 #include "tensorflow/core/platform/env.h"
22 #include "tensorflow/core/platform/file_system_helper.h"
23 
24 namespace tensorflow {
25 namespace {
26 
RemoveSuffix(const string & name,const string & suffix)27 string RemoveSuffix(const string& name, const string& suffix) {
28   string output(name);
29   StringPiece piece(output);
30   absl::ConsumeSuffix(&piece, suffix);
31   return string(piece);
32 }
33 
34 // Closes the given AAsset when variable is destructed.
35 class ScopedAsset {
36  public:
ScopedAsset(AAsset * asset)37   ScopedAsset(AAsset* asset) : asset_(asset) {}
~ScopedAsset()38   ~ScopedAsset() {
39     if (asset_ != nullptr) {
40       AAsset_close(asset_);
41     }
42   }
43 
get() const44   AAsset* get() const { return asset_; }
45 
46  private:
47   AAsset* asset_;
48 };
49 
50 // Closes the given AAssetDir when variable is destructed.
51 class ScopedAssetDir {
52  public:
ScopedAssetDir(AAssetDir * asset_dir)53   ScopedAssetDir(AAssetDir* asset_dir) : asset_dir_(asset_dir) {}
~ScopedAssetDir()54   ~ScopedAssetDir() {
55     if (asset_dir_ != nullptr) {
56       AAssetDir_close(asset_dir_);
57     }
58   }
59 
get() const60   AAssetDir* get() const { return asset_dir_; }
61 
62  private:
63   AAssetDir* asset_dir_;
64 };
65 
66 class ReadOnlyMemoryRegionFromAsset : public ReadOnlyMemoryRegion {
67  public:
ReadOnlyMemoryRegionFromAsset(std::unique_ptr<char[]> data,uint64 length)68   ReadOnlyMemoryRegionFromAsset(std::unique_ptr<char[]> data, uint64 length)
69       : data_(std::move(data)), length_(length) {}
70   ~ReadOnlyMemoryRegionFromAsset() override = default;
71 
data()72   const void* data() override { return reinterpret_cast<void*>(data_.get()); }
length()73   uint64 length() override { return length_; }
74 
75  private:
76   std::unique_ptr<char[]> data_;
77   uint64 length_;
78 };
79 
80 // Note that AAssets are not thread-safe and cannot be used across threads.
81 // However, AAssetManager is. Because RandomAccessFile must be thread-safe and
82 // used across threads, new AAssets must be created for every access.
83 // TODO(tylerrhodes): is there a more efficient way to do this?
84 class RandomAccessFileFromAsset : public RandomAccessFile {
85  public:
RandomAccessFileFromAsset(AAssetManager * asset_manager,const string & name)86   RandomAccessFileFromAsset(AAssetManager* asset_manager, const string& name)
87       : asset_manager_(asset_manager), file_name_(name) {}
88   ~RandomAccessFileFromAsset() override = default;
89 
Read(uint64 offset,size_t to_read,StringPiece * result,char * scratch) const90   Status Read(uint64 offset, size_t to_read, StringPiece* result,
91               char* scratch) const override {
92     auto asset = ScopedAsset(AAssetManager_open(
93         asset_manager_, file_name_.c_str(), AASSET_MODE_RANDOM));
94     if (asset.get() == nullptr) {
95       return errors::NotFound("File ", file_name_, " not found.");
96     }
97 
98     off64_t new_offset = AAsset_seek64(asset.get(), offset, SEEK_SET);
99     off64_t length = AAsset_getLength64(asset.get());
100     if (new_offset < 0) {
101       *result = StringPiece(scratch, 0);
102       return errors::OutOfRange("Read after file end.");
103     }
104     const off64_t region_left =
105         std::min(length - new_offset, static_cast<off64_t>(to_read));
106     int read = AAsset_read(asset.get(), scratch, region_left);
107     if (read < 0) {
108       return errors::Internal("Error reading from asset.");
109     }
110     *result = StringPiece(scratch, region_left);
111     return (region_left == to_read)
112                ? Status::OK()
113                : errors::OutOfRange("Read less bytes than requested.");
114   }
115 
116  private:
117   AAssetManager* asset_manager_;
118   string file_name_;
119 };
120 
121 }  // namespace
122 
AssetManagerFileSystem(AAssetManager * asset_manager,const string & prefix)123 AssetManagerFileSystem::AssetManagerFileSystem(AAssetManager* asset_manager,
124                                                const string& prefix)
125     : asset_manager_(asset_manager), prefix_(prefix) {}
126 
FileExists(const string & fname,TransactionToken * token)127 Status AssetManagerFileSystem::FileExists(const string& fname,
128                                           TransactionToken* token) {
129   string path = RemoveAssetPrefix(fname);
130   auto asset = ScopedAsset(
131       AAssetManager_open(asset_manager_, path.c_str(), AASSET_MODE_RANDOM));
132   if (asset.get() == nullptr) {
133     return errors::NotFound("File ", fname, " not found.");
134   }
135   return Status::OK();
136 }
137 
NewRandomAccessFile(const string & fname,TransactionToken * token,std::unique_ptr<RandomAccessFile> * result)138 Status AssetManagerFileSystem::NewRandomAccessFile(
139     const string& fname, TransactionToken* token,
140     std::unique_ptr<RandomAccessFile>* result) {
141   string path = RemoveAssetPrefix(fname);
142   auto asset = ScopedAsset(
143       AAssetManager_open(asset_manager_, path.c_str(), AASSET_MODE_RANDOM));
144   if (asset.get() == nullptr) {
145     return errors::NotFound("File ", fname, " not found.");
146   }
147   result->reset(new RandomAccessFileFromAsset(asset_manager_, path));
148   return Status::OK();
149 }
150 
NewReadOnlyMemoryRegionFromFile(const string & fname,TransactionToken * token,std::unique_ptr<ReadOnlyMemoryRegion> * result)151 Status AssetManagerFileSystem::NewReadOnlyMemoryRegionFromFile(
152     const string& fname, TransactionToken* token,
153     std::unique_ptr<ReadOnlyMemoryRegion>* result) {
154   string path = RemoveAssetPrefix(fname);
155   auto asset = ScopedAsset(
156       AAssetManager_open(asset_manager_, path.c_str(), AASSET_MODE_STREAMING));
157   if (asset.get() == nullptr) {
158     return errors::NotFound("File ", fname, " not found.");
159   }
160 
161   off64_t start, length;
162   int fd = AAsset_openFileDescriptor64(asset.get(), &start, &length);
163   std::unique_ptr<char[]> data;
164   if (fd >= 0) {
165     data.reset(new char[length]);
166     ssize_t result = pread(fd, data.get(), length, start);
167     if (result < 0) {
168       return errors::Internal("Error reading from file ", fname,
169                               " using 'read': ", result);
170     }
171     if (result != length) {
172       return errors::Internal("Expected size does not match size read: ",
173                               "Expected ", length, " vs. read ", result);
174     }
175     close(fd);
176   } else {
177     length = AAsset_getLength64(asset.get());
178     data.reset(new char[length]);
179     const void* asset_buffer = AAsset_getBuffer(asset.get());
180     if (asset_buffer == nullptr) {
181       return errors::Internal("Error reading ", fname, " from asset manager.");
182     }
183     memcpy(data.get(), asset_buffer, length);
184   }
185   result->reset(new ReadOnlyMemoryRegionFromAsset(std::move(data), length));
186   return Status::OK();
187 }
188 
GetChildren(const string & prefixed_dir,TransactionToken * token,std::vector<string> * r)189 Status AssetManagerFileSystem::GetChildren(const string& prefixed_dir,
190                                            TransactionToken* token,
191                                            std::vector<string>* r) {
192   std::string path = NormalizeDirectoryPath(prefixed_dir);
193   auto dir =
194       ScopedAssetDir(AAssetManager_openDir(asset_manager_, path.c_str()));
195   if (dir.get() == nullptr) {
196     return errors::NotFound("Directory ", prefixed_dir, " not found.");
197   }
198   const char* next_file = AAssetDir_getNextFileName(dir.get());
199   while (next_file != nullptr) {
200     r->push_back(next_file);
201     next_file = AAssetDir_getNextFileName(dir.get());
202   }
203   return Status::OK();
204 }
205 
GetFileSize(const string & fname,TransactionToken * token,uint64 * s)206 Status AssetManagerFileSystem::GetFileSize(const string& fname,
207                                            TransactionToken* token, uint64* s) {
208   // If fname corresponds to a directory, return early. It doesn't map to an
209   // AAsset, and would otherwise return NotFound.
210   if (DirectoryExists(fname)) {
211     *s = 0;
212     return Status::OK();
213   }
214   string path = RemoveAssetPrefix(fname);
215   auto asset = ScopedAsset(
216       AAssetManager_open(asset_manager_, path.c_str(), AASSET_MODE_RANDOM));
217   if (asset.get() == nullptr) {
218     return errors::NotFound("File ", fname, " not found.");
219   }
220   *s = AAsset_getLength64(asset.get());
221   return Status::OK();
222 }
223 
Stat(const string & fname,TransactionToken * token,FileStatistics * stat)224 Status AssetManagerFileSystem::Stat(const string& fname,
225                                     TransactionToken* token,
226                                     FileStatistics* stat) {
227   uint64 size;
228   stat->is_directory = DirectoryExists(fname);
229   TF_RETURN_IF_ERROR(GetFileSize(fname, &size));
230   stat->length = size;
231   return Status::OK();
232 }
233 
NormalizeDirectoryPath(const string & fname)234 string AssetManagerFileSystem::NormalizeDirectoryPath(const string& fname) {
235   return RemoveSuffix(RemoveAssetPrefix(fname), "/");
236 }
237 
RemoveAssetPrefix(const string & name)238 string AssetManagerFileSystem::RemoveAssetPrefix(const string& name) {
239   StringPiece piece(name);
240   absl::ConsumePrefix(&piece, prefix_);
241   return string(piece);
242 }
243 
DirectoryExists(const std::string & fname)244 bool AssetManagerFileSystem::DirectoryExists(const std::string& fname) {
245   std::string path = NormalizeDirectoryPath(fname);
246   auto dir =
247       ScopedAssetDir(AAssetManager_openDir(asset_manager_, path.c_str()));
248   // Note that openDir will return something even if the directory doesn't
249   // exist. Therefore, we need to ensure one file exists in the folder.
250   return AAssetDir_getNextFileName(dir.get()) != NULL;
251 }
252 
GetMatchingPaths(const string & pattern,TransactionToken * token,std::vector<string> * results)253 Status AssetManagerFileSystem::GetMatchingPaths(const string& pattern,
254                                                 TransactionToken* token,
255                                                 std::vector<string>* results) {
256   return internal::GetMatchingPaths(this, Env::Default(), pattern, results);
257 }
258 
NewWritableFile(const string & fname,TransactionToken * token,std::unique_ptr<WritableFile> * result)259 Status AssetManagerFileSystem::NewWritableFile(
260     const string& fname, TransactionToken* token,
261     std::unique_ptr<WritableFile>* result) {
262   return errors::Unimplemented("Asset storage is read only.");
263 }
NewAppendableFile(const string & fname,TransactionToken * token,std::unique_ptr<WritableFile> * result)264 Status AssetManagerFileSystem::NewAppendableFile(
265     const string& fname, TransactionToken* token,
266     std::unique_ptr<WritableFile>* result) {
267   return errors::Unimplemented("Asset storage is read only.");
268 }
DeleteFile(const string & f,TransactionToken * token)269 Status AssetManagerFileSystem::DeleteFile(const string& f,
270                                           TransactionToken* token) {
271   return errors::Unimplemented("Asset storage is read only.");
272 }
CreateDir(const string & d,TransactionToken * token)273 Status AssetManagerFileSystem::CreateDir(const string& d,
274                                          TransactionToken* token) {
275   return errors::Unimplemented("Asset storage is read only.");
276 }
DeleteDir(const string & d,TransactionToken * token)277 Status AssetManagerFileSystem::DeleteDir(const string& d,
278                                          TransactionToken* token) {
279   return errors::Unimplemented("Asset storage is read only.");
280 }
RenameFile(const string & s,const string & t,TransactionToken * token)281 Status AssetManagerFileSystem::RenameFile(const string& s, const string& t,
282                                           TransactionToken* token) {
283   return errors::Unimplemented("Asset storage is read only.");
284 }
285 
286 }  // namespace tensorflow
287