| |
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #include "backend.h" |
| #include "file.h" |
| #include "virtual.h" |
| #include "wasmfs.h" |
|
|
| namespace { |
|
|
| std::string normalize(const std::string& name) { |
| std::string result = name; |
| for (auto& ch : result) { |
| ch = std::tolower(ch); |
| } |
| return result; |
| } |
|
|
| } |
|
|
| namespace wasmfs { |
|
|
| class IgnoreCaseDirectory : public VirtualDirectory { |
|
|
| struct ChildInfo { |
| std::string originalName; |
| std::shared_ptr<File> child; |
| }; |
|
|
| |
| std::map<std::string, ChildInfo> children; |
|
|
| public: |
| IgnoreCaseDirectory(std::shared_ptr<Directory> real, backend_t backend) |
| : VirtualDirectory(real, backend) {} |
|
|
| std::shared_ptr<File> getChild(const std::string& name) override; |
| std::shared_ptr<DataFile> insertDataFile(const std::string& name, |
| mode_t mode) override; |
| std::shared_ptr<Directory> insertDirectory(const std::string& name, |
| mode_t mode) override; |
| std::shared_ptr<Symlink> insertSymlink(const std::string& name, |
| const std::string& target) override; |
| int insertMove(const std::string& name, std::shared_ptr<File> file) override; |
| int removeChild(const std::string& name) override; |
| ssize_t getNumEntries() override { return real->locked().getNumEntries(); } |
| Directory::MaybeEntries getEntries() override; |
| std::string getName(std::shared_ptr<File> file) override; |
| bool maintainsFileIdentity() override { return true; } |
| }; |
|
|
| |
| std::shared_ptr<DataFile> virtualize(std::shared_ptr<DataFile> data, |
| backend_t backend) { |
| return std::make_shared<VirtualDataFile>(data, backend); |
| } |
|
|
| std::shared_ptr<Directory> virtualize(std::shared_ptr<Directory> dir, |
| backend_t backend) { |
| return std::make_shared<IgnoreCaseDirectory>(dir, backend); |
| } |
|
|
| std::shared_ptr<Symlink> virtualize(std::shared_ptr<Symlink> link, |
| backend_t backend) { |
| return std::make_shared<VirtualSymlink>(link, backend); |
| } |
|
|
| std::shared_ptr<File> virtualize(std::shared_ptr<File> file, |
| backend_t backend) { |
| if (auto data = file->dynCast<DataFile>()) { |
| return virtualize(data, backend); |
| } else if (auto dir = file->dynCast<Directory>()) { |
| return virtualize(dir, backend); |
| } else if (auto link = file->dynCast<Symlink>()) { |
| return virtualize(link, backend); |
| } |
| WASMFS_UNREACHABLE("unexpected file kind"); |
| } |
|
|
| std::shared_ptr<File> IgnoreCaseDirectory::getChild(const std::string& name) { |
| auto normalized = normalize(name); |
| if (auto it = children.find(normalized); it != children.end()) { |
| return it->second.child; |
| } |
| auto child = real->locked().getChild(normalized); |
| if (!child) { |
| return nullptr; |
| } |
| child = virtualize(child, getBackend()); |
| children[normalized] = {name, child}; |
| return child; |
| } |
|
|
| std::shared_ptr<DataFile> |
| IgnoreCaseDirectory::insertDataFile(const std::string& name, mode_t mode) { |
| auto normalized = normalize(name); |
| auto file = real->locked().insertDataFile(normalized, mode); |
| if (!file) { |
| return nullptr; |
| } |
| file = virtualize(file, getBackend()); |
| children[normalized] = {name, file}; |
| return file; |
| } |
|
|
| std::shared_ptr<Directory> |
| IgnoreCaseDirectory::insertDirectory(const std::string& name, mode_t mode) { |
| auto normalized = normalize(name); |
| auto dir = real->locked().insertDirectory(normalized, mode); |
| if (!dir) { |
| return nullptr; |
| } |
| dir = virtualize(dir, getBackend()); |
| children[normalized] = {name, dir}; |
| return dir; |
| } |
|
|
| std::shared_ptr<Symlink> |
| IgnoreCaseDirectory::insertSymlink(const std::string& name, |
| const std::string& target) { |
| auto normalized = normalize(name); |
| auto link = real->locked().insertSymlink(normalized, target); |
| if (!link) { |
| return nullptr; |
| } |
| link = virtualize(link, getBackend()); |
| children[normalized] = {name, link}; |
| return link; |
| } |
|
|
| int IgnoreCaseDirectory::insertMove(const std::string& name, |
| std::shared_ptr<File> file) { |
| auto normalized = normalize(name); |
| if (auto err = real->locked().insertMove(normalized, devirtualize(file))) { |
| return err; |
| } |
| auto oldParent = |
| std::static_pointer_cast<IgnoreCaseDirectory>(file->locked().getParent()); |
| auto& oldChildren = oldParent->children; |
| |
| for (auto it = oldChildren.begin(); it != oldChildren.end(); ++it) { |
| if (it->second.child == file) { |
| oldChildren.erase(it); |
| break; |
| } |
| } |
| |
| auto [it, inserted] = children.insert({normalized, {name, file}}); |
| if (!inserted) { |
| it->second.child->locked().setParent(nullptr); |
| it->second = {name, file}; |
| } |
|
|
| return 0; |
| } |
|
|
| int IgnoreCaseDirectory::removeChild(const std::string& name) { |
| auto normalized = normalize(name); |
| if (auto err = real->locked().removeChild(normalized)) { |
| return err; |
| } |
| auto it = children.find(normalized); |
| assert(it != children.end()); |
| it->second.child->locked().setParent(nullptr); |
| children.erase(it); |
| return 0; |
| } |
|
|
| Directory::MaybeEntries IgnoreCaseDirectory::getEntries() { |
| auto entries = real->locked().getEntries(); |
| if (entries.getError()) { |
| return entries; |
| } |
| for (auto& entry : *entries) { |
| if (auto it = children.find(entry.name); it != children.end()) { |
| entry.name = it->second.originalName; |
| } |
| } |
| return entries; |
| } |
|
|
| std::string IgnoreCaseDirectory::getName(std::shared_ptr<File> file) { |
| for (auto& [_, info] : children) { |
| if (info.child == file) { |
| return info.originalName; |
| } |
| } |
| return ""; |
| } |
|
|
| class IgnoreCaseBackend : public Backend { |
| backend_t backend; |
|
|
| public: |
| IgnoreCaseBackend(std::function<backend_t()> createBackend) { |
| backend = createBackend(); |
| } |
|
|
| std::shared_ptr<DataFile> createFile(mode_t mode) override { |
| return virtualize(backend->createFile(mode), this); |
| } |
|
|
| std::shared_ptr<Directory> createDirectory(mode_t mode) override { |
| auto real = backend->createDirectory(mode); |
| |
| |
| |
| real->locked().setParent(real); |
| auto ret = virtualize(real, this); |
| return ret; |
| } |
|
|
| std::shared_ptr<Symlink> createSymlink(std::string target) override { |
| return virtualize(backend->createSymlink(target), this); |
| } |
| }; |
|
|
| |
| backend_t createIgnoreCaseBackend(std::function<backend_t()> createBackend) { |
| return wasmFS.addBackend(std::make_unique<IgnoreCaseBackend>(createBackend)); |
| } |
|
|
| extern "C" { |
|
|
| |
| |
| backend_t wasmfs_create_icase_backend(backend_t backend) { |
| return createIgnoreCaseBackend([backend]() { return backend; }); |
| } |
|
|
| } |
|
|
| } |
|
|