|  | // Copyright 2018 The Fuchsia Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "src/ui/lib/escher/fs/fuchsia_data_source.h" | 
|  |  | 
|  | #include <lib/vfs/cpp/pseudo_dir.h> | 
|  | #include <lib/vfs/cpp/pseudo_file.h> | 
|  | #include <zircon/errors.h> | 
|  |  | 
|  | #include <memory> | 
|  | #include <string> | 
|  | #include <vector> | 
|  |  | 
|  | #include "src/lib/files/directory.h" | 
|  |  | 
|  | namespace escher { | 
|  | namespace { | 
|  |  | 
|  | std::vector<std::string> StrSplit(const std::string& str, const std::string& delim) { | 
|  | std::vector<std::string> items; | 
|  | for (size_t start = 0; start < str.length();) { | 
|  | size_t end = str.find(delim, start); | 
|  | if (end == std::string::npos) { | 
|  | end = str.length(); | 
|  | } | 
|  | items.push_back(str.substr(start, end - start)); | 
|  | start = end + delim.length(); | 
|  | } | 
|  | return items; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | FuchsiaDataSource::FuchsiaDataSource(const std::shared_ptr<vfs::PseudoDir>& root_dir) | 
|  | : root_dir_(root_dir) {} | 
|  |  | 
|  | FuchsiaDataSource::FuchsiaDataSource() : root_dir_(std::make_shared<vfs::PseudoDir>()) {} | 
|  |  | 
|  | bool FuchsiaDataSource::InitializeWithRealFiles(const std::vector<HackFilePath>& paths, | 
|  | const char* root) { | 
|  | const std::string kRoot(root); | 
|  | base_path_ = kRoot; | 
|  | bool success = true; | 
|  | for (const auto& path : paths) { | 
|  | success &= LoadFile(this, kRoot, path); | 
|  |  | 
|  | auto segs = StrSplit(path, "/"); | 
|  | FX_DCHECK(segs.size() > 0); | 
|  | auto dir = root_dir_.get(); | 
|  | for (size_t i = 0; i + 1 < segs.size(); ++i) { | 
|  | const auto& seg = segs[i]; | 
|  | vfs::internal::Node* subdir; | 
|  | if (ZX_OK != dir->Lookup(seg, &subdir)) { | 
|  | auto node = std::make_unique<vfs::PseudoDir>(); | 
|  | subdir = node.get(); | 
|  | auto status = dir->AddEntry(seg, std::move(node)); | 
|  | FX_DCHECK(ZX_OK == status); | 
|  | if (status != ZX_OK) { | 
|  | return false;  // don't hang the system | 
|  | } | 
|  | } | 
|  | dir = static_cast<vfs::PseudoDir*>(subdir); | 
|  | } | 
|  | zx_status_t status = dir->AddEntry( | 
|  | segs[segs.size() - 1], | 
|  | std::make_unique<vfs::PseudoFile>( | 
|  | 200 * 1024 * 1024 /* max file size, 200 MB */, | 
|  | /* read_handler= */ | 
|  | [this, path](std::vector<uint8_t>* output, size_t max_file_size) { | 
|  | auto out = ReadFile(path); | 
|  | size_t len = out.length(); | 
|  | if (len > max_file_size) { | 
|  | FX_LOGS(WARNING) << "File(" << path << ") size more than: " << max_file_size | 
|  | << ", truncating"; | 
|  | len = max_file_size; | 
|  | } | 
|  | output->resize(len); | 
|  | std::copy(out.begin(), out.begin() + len, output->begin()); | 
|  | return ZX_OK; | 
|  | }, | 
|  | /* write_handler= */ | 
|  | [this, path](std::vector<uint8_t> input) { | 
|  | // TODO(fxbug.dev/7189): The file is successfully updated, but the | 
|  | // terminal would complain "truncate: Invalid argument". | 
|  | HackFileContents content(input.size(), 0); | 
|  | std::copy(input.begin(), input.begin() + input.size(), content.begin()); | 
|  | FX_LOGS(INFO) << "Updated file: " << path; | 
|  | WriteFile(path, std::move(content)); | 
|  | return ZX_OK; | 
|  | })); | 
|  |  | 
|  | if (status != ZX_OK && status != ZX_ERR_ALREADY_EXISTS) { | 
|  | FX_LOGS(WARNING) << "Failed to AddEntry(): " << status; | 
|  | success = false; | 
|  | } | 
|  | } | 
|  | return success; | 
|  | } | 
|  |  | 
|  | }  // namespace escher |