OLD | NEW |
| (Empty) |
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 #ifndef PRODUCT | |
6 | |
7 #include <map> | |
8 #include <string> | |
9 #include <vector> | |
10 | |
11 #include "vm/dev_fs.h" | |
12 | |
13 #include "vm/hash_table.h" | |
14 #include "vm/json_stream.h" | |
15 #include "vm/lockers.h" | |
16 #include "vm/object.h" | |
17 #include "vm/unicode.h" | |
18 | |
19 namespace dart { | |
20 | |
21 static const uint8_t decode_table[256] = { | |
22 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, | |
23 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, | |
24 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, | |
25 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, | |
26 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, | |
27 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64, | |
28 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, | |
29 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, | |
30 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, | |
31 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, | |
32 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, | |
33 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, | |
34 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, | |
35 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, | |
36 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, | |
37 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 | |
38 }; | |
39 | |
40 class Base64 { | |
41 public: | |
42 static void decode(const char* base64, | |
43 std::vector<uint8_t>* output) { | |
44 ASSERT(output != NULL); | |
45 ASSERT(base64 != NULL); | |
46 const intptr_t base64_len = strlen(base64); | |
47 int b[4]; | |
48 for (intptr_t i = 0; i < base64_len; i += 4) { | |
49 b[0] = decode_table[static_cast<uint8_t>(base64[i])]; | |
50 b[1] = decode_table[static_cast<uint8_t>(base64[i + 1])]; | |
51 b[2] = decode_table[static_cast<uint8_t>(base64[i + 2])]; | |
52 b[3] = decode_table[static_cast<uint8_t>(base64[i + 3])]; | |
53 output->push_back((b[0] << 2) | (b[1] >> 4)); | |
54 if (b[2] < 64) { | |
55 output->push_back((b[1] << 4) | (b[2] >> 2)); | |
56 if (b[3] < 64) { | |
57 output->push_back((b[2] << 6) | b[3]); | |
58 } | |
59 } | |
60 } | |
61 } | |
62 }; | |
63 | |
64 | |
65 class FileSystem { | |
66 public: | |
67 explicit FileSystem(const std::string& name) | |
68 : name_(name) { | |
69 } | |
70 | |
71 ~FileSystem() { | |
72 } | |
73 | |
74 bool ReadFile(const std::string& path, | |
75 std::vector<uint8_t>** file_contents) { | |
76 *file_contents = NULL; | |
77 std::map<std::string, std::vector<uint8_t>*>::iterator iter; | |
78 iter = files_.find(path); | |
79 if (iter == files_.end()) { | |
80 return false; | |
81 } | |
82 *file_contents = iter->second; | |
83 return true; | |
84 } | |
85 | |
86 void DeleteFile(const std::string& path) { | |
87 std::map<std::string, std::vector<uint8_t>*>::iterator iter; | |
88 iter = files_.find(path); | |
89 if (iter == files_.end()) { | |
90 return; | |
91 } | |
92 std::vector<uint8_t>* contents = iter->second; | |
93 files_.erase(iter); | |
94 delete contents; | |
95 } | |
96 | |
97 void WriteFile(const std::string& path, | |
98 const char* file_contents) { | |
99 DeleteFile(path); | |
100 std::vector<uint8_t>* data = new std::vector<uint8_t>(); | |
101 Base64::decode(file_contents, data); | |
102 files_[path] = data; | |
103 } | |
104 | |
105 void ListFiles(JSONStream* js) { | |
106 JSONObject jsobj(js); | |
107 jsobj.AddProperty("type", "FSFilesList"); | |
108 JSONArray jsarr(&jsobj, "files"); | |
109 std::map<std::string, std::vector<uint8_t>*>::iterator iter; | |
110 for (iter = files_.begin(); iter != files_.end(); iter++) { | |
111 JSONObject file_info(&jsarr); | |
112 file_info.AddProperty("name", iter->first.c_str()); | |
113 file_info.AddProperty64("size", | |
114 static_cast<int64_t>(iter->second->size())); | |
115 } | |
116 } | |
117 | |
118 private: | |
119 std::string name_; | |
120 | |
121 std::map<std::string, std::vector<uint8_t>*> files_; | |
122 }; | |
123 | |
124 // Some static state is held outside of the DevFS class so that we don't | |
125 // have to include stl headers in our vm/ headers. | |
126 static std::map<std::string, FileSystem*>* file_systems_; | |
127 | |
128 Mutex* DevFS::mutex_ = NULL; | |
129 | |
130 | |
131 void DevFS::Init() { | |
132 if (mutex_ != NULL) { | |
133 // Already initialized. | |
134 ASSERT(file_systems_ != NULL); | |
135 return; | |
136 } | |
137 mutex_ = new Mutex(); | |
138 file_systems_ = new std::map<std::string, FileSystem*>(); | |
139 ASSERT(mutex_ != NULL); | |
140 ASSERT(file_systems_ != NULL); | |
141 } | |
142 | |
143 | |
144 void DevFS::Cleanup() { | |
145 delete mutex_; | |
146 mutex_ = NULL; | |
147 std::map<std::string, FileSystem*>::iterator iter; | |
148 for (iter = file_systems_->begin(); iter != file_systems_->end(); iter++) { | |
149 FileSystem* fs = iter->second; | |
150 delete fs; | |
151 } | |
152 delete file_systems_; | |
153 file_systems_ = NULL; | |
154 } | |
155 | |
156 | |
157 void DevFS::ListFileSystems(JSONStream* js) { | |
158 SafepointMutexLocker ml(mutex_); | |
159 JSONObject jsobj(js); | |
160 jsobj.AddProperty("type", "FSList"); | |
161 JSONArray jsarr(&jsobj, "fsNames"); | |
162 | |
163 std::map<std::string, FileSystem*>::iterator iter; | |
164 for (iter = file_systems_->begin(); iter != file_systems_->end(); iter++) { | |
165 const std::string& key = iter->first; | |
166 jsarr.AddValue(key.c_str()); | |
167 } | |
168 } | |
169 | |
170 | |
171 FileSystem* DevFS::LookupFileSystem(const char* fs_name) { | |
172 std::string key = std::string(fs_name); | |
173 std::map<std::string, FileSystem*>::iterator iter; | |
174 iter = file_systems_->find(key); | |
175 if (iter != file_systems_->end()) { | |
176 return iter->second; | |
177 } | |
178 return NULL; | |
179 } | |
180 | |
181 | |
182 FileSystem* DevFS::LookupFileSystem(const String& fs_name) { | |
183 return LookupFileSystem(fs_name.ToCString()); | |
184 } | |
185 | |
186 | |
187 void DevFS::CreateFileSystem(JSONStream* js, const String& fs_name) { | |
188 SafepointMutexLocker ml(mutex_); | |
189 // TODO(turnidge): Ensure that fs_name is a legal URI host value, i.e. ascii. | |
190 if (LookupFileSystem(fs_name) != NULL) { | |
191 js->PrintError(kFileSystemAlreadyExists, | |
192 "%s: file system '%s' already exists", | |
193 js->method(), fs_name.ToCString()); | |
194 return; | |
195 } | |
196 | |
197 std::string key = std::string(fs_name.ToCString()); | |
198 FileSystem* file_system = new FileSystem(key); | |
199 (*file_systems_)[key] = file_system; | |
200 | |
201 JSONObject jsobj(js); | |
202 jsobj.AddProperty("type", "Success"); | |
203 } | |
204 | |
205 | |
206 void DevFS::DeleteFileSystem(JSONStream* js, const String& fs_name) { | |
207 SafepointMutexLocker ml(mutex_); | |
208 FileSystem* file_system = LookupFileSystem(fs_name); | |
209 if (file_system == NULL) { | |
210 js->PrintError(kFileSystemDoesNotExist, | |
211 "%s: file system '%s' does not exist", | |
212 js->method(), fs_name.ToCString()); | |
213 return; | |
214 } | |
215 std::string key = std::string(fs_name.ToCString()); | |
216 file_systems_->erase(key); | |
217 delete file_system; | |
218 JSONObject jsobj(js); | |
219 jsobj.AddProperty("type", "Success"); | |
220 } | |
221 | |
222 | |
223 void DevFS::ListFiles(JSONStream* js, const String& fs_name) { | |
224 SafepointMutexLocker ml(mutex_); | |
225 FileSystem* file_system = LookupFileSystem(fs_name); | |
226 if (file_system == NULL) { | |
227 js->PrintError(kFileSystemDoesNotExist, | |
228 "%s: file system '%s' does not exist", | |
229 js->method(), fs_name.ToCString()); | |
230 return; | |
231 } | |
232 | |
233 file_system->ListFiles(js); | |
234 } | |
235 | |
236 | |
237 static void PrintWriteFilesError(JSONStream* js, | |
238 intptr_t i) { | |
239 js->PrintError(kInvalidParams, | |
240 "%s: files array invalid at index '%" Pd "'", | |
241 js->method(), i); | |
242 } | |
243 | |
244 | |
245 void DevFS::WriteFiles(JSONStream* js, | |
246 const String& fs_name, | |
247 const Array& files) { | |
248 SafepointMutexLocker ml(mutex_); | |
249 FileSystem* file_system = LookupFileSystem(fs_name); | |
250 if (file_system == NULL) { | |
251 js->PrintError(kFileSystemDoesNotExist, | |
252 "%s: file system '%s' does not exist", | |
253 js->method(), fs_name.ToCString()); | |
254 return; | |
255 } | |
256 | |
257 Object& test = Object::Handle(); | |
258 GrowableObjectArray& file_info = GrowableObjectArray::Handle(); | |
259 String& path = String::Handle(); | |
260 String& file_contents = String::Handle(); | |
261 | |
262 // First, validate the array of files is properly formed. | |
263 for (intptr_t i = 0; i < files.Length(); i++) { | |
264 test = files.At(i); | |
265 if (!test.IsGrowableObjectArray()) { | |
266 PrintWriteFilesError(js, i); | |
267 return; | |
268 } | |
269 file_info ^= test.raw(); | |
270 if (file_info.Length() != 2) { | |
271 PrintWriteFilesError(js, i); | |
272 return; | |
273 } | |
274 test = file_info.At(0); | |
275 if (!test.IsString()) { | |
276 PrintWriteFilesError(js, i); | |
277 return; | |
278 } | |
279 std::string key = std::string(String::Cast(test).ToCString()); | |
280 if ((key.size() == 0) || (key[0] != '/')) { | |
281 js->PrintError(kInvalidParams, | |
282 "%s: file system path '%s' must begin with a /", | |
283 js->method(), String::Cast(test).ToCString()); | |
284 return; | |
285 } | |
286 test = file_info.At(1); | |
287 if (!test.IsString()) { | |
288 PrintWriteFilesError(js, i); | |
289 return; | |
290 } | |
291 } | |
292 | |
293 // Now atomically update the file system. | |
294 for (intptr_t i = 0; i < files.Length(); i++) { | |
295 file_info = GrowableObjectArray::RawCast(files.At(i)); | |
296 path = String::RawCast(file_info.At(0)); | |
297 file_contents = String::RawCast(file_info.At(1)); | |
298 file_system->WriteFile(path.ToCString(), | |
299 file_contents.ToCString()); | |
300 } | |
301 | |
302 JSONObject jsobj(js); | |
303 jsobj.AddProperty("type", "Success"); | |
304 } | |
305 | |
306 | |
307 void DevFS::WriteFile(JSONStream* js, | |
308 const String& fs_name, | |
309 const String& path, | |
310 const String& file_contents) { | |
311 SafepointMutexLocker ml(mutex_); | |
312 FileSystem* file_system = LookupFileSystem(fs_name); | |
313 if (file_system == NULL) { | |
314 js->PrintError(kFileSystemDoesNotExist, | |
315 "%s: file system '%s' does not exist", | |
316 js->method(), fs_name.ToCString()); | |
317 return; | |
318 } | |
319 | |
320 std::string key = std::string(path.ToCString()); | |
321 if ((key.size() == 0) || (key[0] != '/')) { | |
322 js->PrintError(kInvalidParams, | |
323 "%s: file system path '%s' must begin with a /", | |
324 js->method(), path.ToCString()); | |
325 return; | |
326 } | |
327 | |
328 file_system->WriteFile(path.ToCString(), | |
329 file_contents.ToCString()); | |
330 | |
331 JSONObject jsobj(js); | |
332 jsobj.AddProperty("type", "Success"); | |
333 } | |
334 | |
335 | |
336 void DevFS::ReadFile(JSONStream* js, | |
337 const String& fs_name, | |
338 const String& path) { | |
339 SafepointMutexLocker ml(mutex_); | |
340 FileSystem* file_system = LookupFileSystem(fs_name); | |
341 if (file_system == NULL) { | |
342 js->PrintError(kFileSystemDoesNotExist, | |
343 "%s: file system '%s' does not exist", | |
344 js->method(), fs_name.ToCString()); | |
345 return; | |
346 } | |
347 | |
348 std::string key = std::string(path.ToCString()); | |
349 if ((key.size() == 0) || (key[0] != '/')) { | |
350 js->PrintError(kInvalidParams, | |
351 "%s: file system path '%s' must begin with a /", | |
352 js->method(), path.ToCString()); | |
353 return; | |
354 } | |
355 std::vector<uint8_t>* file_contents; | |
356 | |
357 bool success = file_system->ReadFile(key, &file_contents); | |
358 | |
359 if (!success) { | |
360 js->PrintError(kFileDoesNotExist, | |
361 "%s: file 'dart-devfs://%s/%s' does not exist", | |
362 js->method(), fs_name.ToCString(), path.ToCString()); | |
363 return; | |
364 } | |
365 | |
366 JSONObject jsobj(js); | |
367 jsobj.AddProperty("type", "FSFile"); | |
368 jsobj.AddPropertyBase64("fileContents", | |
369 &((*file_contents)[0]), | |
370 file_contents->size()); | |
371 } | |
372 | |
373 } // namespace dart | |
374 | |
375 #endif // !PRODUCT | |
OLD | NEW |