| 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 |