#include "TessesFramework/Filesystem/MemoryFilesystem.hpp" #include namespace Tesses::Framework::Filesystem { MemoryFilesystemStream::MemoryFilesystemStream(std::shared_ptr mtx, std::shared_ptr data,bool canRead, bool canWrite, bool canSeek) { this->mtx = mtx; this->data = data; this->canRead = canRead; this->canWrite = canWrite; this->canSeek = canSeek; this->pos=0; } size_t MemoryFilesystemStream::Read(uint8_t* buff, size_t sz) { if(!this->canRead) return 0; if(this->pos >= this->data->file.size()) return 0; size_t toRead = std::min(sz, (size_t)(this->data->file.size()-this->pos)); memcpy(buff, this->data->file.data() + this->pos, toRead); this->pos += toRead; return toRead; } size_t MemoryFilesystemStream::Write(const uint8_t* buff, size_t sz) { if(!this->canWrite) return 0; if(this->canSeek) { if(this->pos > this->data->file.size()) { this->data->file.resize(this->pos+sz); } this->data->file.insert(this->data->file.begin()+this->pos, buff, buff+sz); this->pos+=sz; } else { this->data->file.insert(this->data->file.end(), buff, buff+sz); } return sz; } bool MemoryFilesystemStream::CanRead() { return canRead; } bool MemoryFilesystemStream::CanWrite() { return canWrite; } bool MemoryFilesystemStream::CanSeek() { return canSeek; } int64_t MemoryFilesystemStream::GetPosition() { if(!this->canSeek) return (int64_t)this->data->file.size(); return pos; } void MemoryFilesystemStream::Flush() { //its already flushed } void MemoryFilesystemStream::Seek(int64_t pos, Streams::SeekOrigin whence) { if(canSeek) return; switch(whence) { case Streams::SeekOrigin::Begin: this->pos = (size_t)pos; break; case Streams::SeekOrigin::Current: this->pos += (size_t)pos; break; case Streams::SeekOrigin::End: this->pos = (size_t)(this->data->file.size() + pos); break; } } MemoryFilesystemStream::~MemoryFilesystemStream() { mtx->Lock(); if(this->canWrite) this->data->canAccess=true; this->data->readers--; mtx->Unlock(); } MemoryEntry* MemoryFilesystem::GetEntry(VFSPath path,bool followSymlink) { if(path.relative) return nullptr; if(path.path.empty()) return &this->root; auto entry = GetEntry(path.GetParent(),true); if(entry == nullptr) return nullptr; auto dir = dynamic_cast(entry); if(dir != nullptr) { for(auto item : dir->entries) { if(item->name == path.GetFileName()) { auto link = dynamic_cast(item); if(followSymlink && link != nullptr) { item = GetEntry(link->linkedTo,true); } return item; } } } return nullptr; } Streams::Stream* MemoryFilesystem::OpenFile(VFSPath path, std::string mode) { bool canRead=false; bool canWrite=false; bool canSeek=true; bool mustExist=false; bool truncate=false; if(mode.size() >= 1) { if(mode[0] == 'r') { canRead = true; mustExist=true; } else if(mode[0] == 'w') { canWrite = true; truncate=true; } else if(mode[0] == 'a') { canSeek = false; canWrite = true; } } if(((mode.size() >= 2 && mode[1] == '+') || (mode.size() >= 2 && mode[1] == 'b' && mode[2] == '+'))) { canRead = true; canWrite = true; } mtx->Lock(); if(mustExist) { auto file = GetEntry(path,true); if(file == nullptr) { mtx->Unlock(); return nullptr; } auto f = dynamic_cast(file); if(f == nullptr) { mtx->Unlock(); return nullptr; } if(!f->data->canAccess) { mtx->Unlock(); return nullptr; } if(canWrite && f->data->readers > 0) { mtx->Unlock(); return nullptr; } f->data->readers++; if(canWrite) f->data->canAccess=false; mtx->Unlock(); return new MemoryFilesystemStream(mtx,f->data,canRead,canWrite,canSeek); } else { auto file = dynamic_cast(GetEntry(path,true)); if(file != nullptr) { if(!file->data->canAccess) { mtx->Unlock(); return nullptr; } if(file->data->readers > 0) { mtx->Unlock(); return nullptr; } file->data->canAccess=false; file->data->readers++; if(truncate) {file->data->file.clear(); file->data->lastWrite=Date::DateTime::NowUTC();} mtx->Unlock(); return new MemoryFilesystemStream(mtx,file->data,canRead,canWrite,canSeek); } auto dir = GetEntry(path.GetParent(),true); if(dir == nullptr) { mtx->Unlock(); return nullptr; } auto myDir = dynamic_cast(dir); if(myDir == nullptr) { mtx->Unlock(); return nullptr; } MemoryFile* thefile=nullptr; for(auto f : myDir->entries) { if(f->name == path.GetFileName()) { auto symlink = dynamic_cast(f); while(symlink != nullptr) { auto ent = GetEntry(symlink->name,false); auto sym = dynamic_cast(f); if(sym != nullptr) symlink = sym; else { auto myDir0 = dynamic_cast(GetEntry(symlink->linkedTo.GetParent(),true)); if(myDir0 != nullptr) { for(auto f2 : myDir0->entries) { if(f2->name == symlink->linkedTo.GetFileName()) { auto myFile = dynamic_cast(f2); if(myFile != nullptr) { thefile = myFile; break; } else { mtx->Unlock(); return nullptr; } } } myDir = myDir0; } else { mtx->Unlock(); return nullptr; } break; } } break; } } if(thefile == nullptr) { MemoryFile* f = new MemoryFile(); f->name = path.GetFileName(); f->data = std::make_shared(); f->data->canAccess=false; f->data->readers++; myDir->entries.push_back(f); mtx->Unlock(); return new MemoryFilesystemStream(mtx,f->data,canRead,canWrite,canSeek); } if(thefile != nullptr) { if(!thefile->data->canAccess) { mtx->Unlock(); return nullptr; } if(thefile->data->readers > 0) { mtx->Unlock(); return nullptr; } thefile->data->canAccess=false; thefile->data->readers++; if(truncate) {thefile->data->file.clear(); thefile->data->lastWrite=Date::DateTime::NowUTC();} mtx->Unlock(); return new MemoryFilesystemStream(mtx,thefile->data,canRead,canWrite,canSeek); } } mtx->Unlock(); return nullptr; } void MemoryFilesystem::CreateDirectory(VFSPath path) { if(path.relative) return; if(path.path.empty()) return; mtx->Lock(); MemoryDirectory* dir=&root; for(auto part : path.path) { bool have=false; for(auto dirent : dir->entries) { if(dirent->name == part) { auto symlink = dynamic_cast(dirent); if(symlink != nullptr) { dirent = GetEntry(symlink->linkedTo,true); } auto dirdirent = dynamic_cast(dirent); if(dirdirent != nullptr) { dir = dirdirent; have=true; break; } else { mtx->Unlock(); return; } } } if(!have) { MemoryDirectory* dir2 = new MemoryDirectory(); dir2->name = part; dir2->lastWrite=Date::DateTime::NowUTC(); dir->entries.push_back(dir2); dir->lastWrite=Date::DateTime::NowUTC(); dir=dir2; } } mtx->Unlock(); } void MemoryFilesystem::DeleteFile(VFSPath path) { if(path.relative || path.path.empty()) return; mtx->Lock(); MemoryDirectory* dir=&root; if(path.path.size() > 1) { dir = dynamic_cast(GetEntry(path.GetParent(),true)); } if(dir == nullptr) { mtx->Unlock(); return; } std::string fname = path.GetFileName(); for(auto index = dir->entries.begin(); index < dir->entries.end(); index++) { auto item = *index; if(item->name == fname) { auto p = dynamic_cast(item); if(p == nullptr) { delete item; dir->entries.erase(index); dir->lastWrite=Date::DateTime::NowUTC(); } break; } } mtx->Unlock(); } bool MemoryFilesystem::RegularFileExists(VFSPath path) { if(path.relative) return false; if(path.path.empty()) return false; mtx->Lock(); auto f = GetEntry(path,false); mtx->Unlock(); return dynamic_cast(f) != nullptr; } bool MemoryFilesystem::SymlinkExists(VFSPath path) { if(path.relative) return false; if(path.path.empty()) return false; mtx->Lock(); auto f = GetEntry(path,false); mtx->Unlock(); return dynamic_cast(f) != nullptr; } bool MemoryFilesystem::DirectoryExists(VFSPath path) { if(path.relative) return false; if(path.path.empty()) return true; mtx->Lock(); auto f = GetEntry(path,false); mtx->Unlock(); return dynamic_cast(f) != nullptr; } void MemoryFilesystem::DeleteDirectory(VFSPath path) { if(path.relative || path.path.empty()) return; mtx->Lock(); MemoryDirectory* dir=&root; if(path.path.size() > 1) { dir = dynamic_cast(GetEntry(path.GetParent(),true)); } if(dir == nullptr) { mtx->Unlock(); return; } std::string fname = path.GetFileName(); for(auto index = dir->entries.begin(); index < dir->entries.end(); index++) { auto item = *index; if(item->name == fname) { auto p = dynamic_cast(item); if(p != nullptr) { delete item; dir->entries.erase(index); dir->lastWrite=Date::DateTime::NowUTC(); } break; } } mtx->Unlock(); } void MemoryFilesystem::CreateSymlink(VFSPath existingFile, VFSPath symlinkFile) { if(existingFile.relative || symlinkFile.relative || symlinkFile.path.empty()) return; mtx->Lock(); MemoryDirectory* dir=&root; if(symlinkFile.path.size() > 1) { dir = dynamic_cast(GetEntry(symlinkFile.GetParent(),true)); } if(dir == nullptr) { mtx->Unlock(); return; } std::string fname = symlinkFile.GetFileName(); for(auto index = dir->entries.begin(); index < dir->entries.end(); index++) { auto item = *index; if(item->name == fname) { auto p = dynamic_cast(item); if(p != nullptr) { p->linkedTo = existingFile; p->lastWrite = Date::DateTime::NowUTC(); } mtx->Unlock(); return; } } MemorySymlink* symlink = new MemorySymlink(); symlink->name = fname; symlink->linkedTo = existingFile; symlink->lastWrite = Date::DateTime::NowUTC(); dir->entries.push_back(symlink); dir->lastWrite = Date::DateTime::NowUTC(); mtx->Unlock(); } VFSPathEnumerator MemoryFilesystem::EnumeratePaths(VFSPath path) { std::pair>* paths=new std::pair>(); paths->first=0; mtx->Lock(); auto dir = dynamic_cast(GetEntry(path,true)); if(dir != nullptr) { for(auto item : dir->entries) paths->second.push_back(item->name); } mtx->Unlock(); return VFSPathEnumerator([paths,path](VFSPath& _path)->bool{ if(paths->first < paths->second.size()) { _path = path / paths->second[paths->first++]; return true; } return false; },[paths]()->void{ delete paths; }); } void MemoryFilesystem::CreateHardlink(VFSPath existingFile, VFSPath newName) { mtx->Lock(); auto existing = dynamic_cast(GetEntry(existingFile,true)); if(existing == nullptr) { mtx->Unlock(); return; } MemoryDirectory* dir=&root; if(newName.path.size() > 1) { dir = dynamic_cast(GetEntry(newName.GetParent(),true)); } if(dir == nullptr) { mtx->Unlock(); return; } std::string fname = newName.GetFileName(); for(auto index = dir->entries.begin(); index < dir->entries.end(); index++) { auto item = *index; if(item->name == fname) { mtx->Unlock(); return; } } MemoryFile* memFile = new MemoryFile(); memFile->name = fname; memFile->data = existing->data; dir->entries.push_back(memFile); dir->lastWrite=Date::DateTime::NowUTC(); mtx->Unlock(); } void MemoryFilesystem::MoveFile(VFSPath src, VFSPath dest) { DeleteFile(dest); CreateHardlink(src,dest); DeleteFile(src); } void MemoryFilesystem::MoveDirectory(VFSPath src, VFSPath dest) { CreateDirectory(dest); for(auto ent : EnumeratePaths(src)) { VFSPath destPath = dest / ent.GetFileName(); if(FileExists(ent)) MoveFile(ent,destPath); if(DirectoryExists(ent)) MoveDirectory(ent,destPath); } DeleteDirectory(src); } VFSPath MemoryFilesystem::ReadLink(VFSPath p) { mtx->Lock(); VFSPath p2; auto symlink = dynamic_cast(GetEntry(p,false)); if(symlink != nullptr) { p2 = symlink->linkedTo; } mtx->Unlock(); return p2; } std::string MemoryFilesystem::VFSPathToSystem(VFSPath path) { return path.ToString(); } VFSPath MemoryFilesystem::SystemToVFSPath(std::string path) { return path; } void MemoryFilesystem::GetDate(VFSPath path, Date::DateTime& lastWrite, Date::DateTime& lastAccess) { mtx->Lock(); auto node = GetEntry(path,false); auto dir = dynamic_cast(node); auto file = dynamic_cast(node); auto sym = dynamic_cast(node); if(dir != nullptr) lastWrite = dir->lastWrite; if(file != nullptr) lastWrite = file->data->lastWrite; if(sym != nullptr) lastWrite = sym->lastWrite; mtx->Unlock(); lastAccess = lastWrite; } void MemoryFilesystem::SetDate(VFSPath path, Date::DateTime lastWrite, Date::DateTime lastAccess) { mtx->Lock(); auto node = GetEntry(path,false); auto dir = dynamic_cast(node); auto file = dynamic_cast(node); auto sym = dynamic_cast(node); if(dir != nullptr) dir->lastWrite = lastWrite; if(file != nullptr) file->data->lastWrite = lastWrite; if(sym != nullptr) sym->lastWrite = lastWrite; mtx->Unlock(); } MemoryFilesystem::~MemoryFilesystem() { } MemoryFilesystem::MemoryFilesystem() { mtx = std::make_shared(); } MemoryEntry::~MemoryEntry() { } MemoryFile::~MemoryFile() { } MemoryDirectory::MemoryDirectory() { this->lastWrite = Date::DateTime::NowUTC(); } MemoryDirectory::~MemoryDirectory() { for(auto item : this->entries) delete item; } MemoryFileData::MemoryFileData() { this->lastWrite = Date::DateTime::NowUTC(); this->canAccess=true; this->readers=0; } }