669 lines
20 KiB
C++
669 lines
20 KiB
C++
#include "TessesFramework/Filesystem/MemoryFilesystem.hpp"
|
|
#include <iostream>
|
|
|
|
namespace Tesses::Framework::Filesystem
|
|
{
|
|
MemoryFilesystemStream::MemoryFilesystemStream(std::shared_ptr<Tesses::Framework::Threading::Mutex> mtx, std::shared_ptr<MemoryFileData> 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<MemoryDirectory*>(entry);
|
|
if(dir != nullptr)
|
|
{
|
|
for(auto item : dir->entries)
|
|
{
|
|
if(item->name == path.GetFileName())
|
|
{
|
|
auto link = dynamic_cast<MemorySymlink*>(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<MemoryFile*>(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<MemoryFile*>(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<MemoryDirectory*>(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<MemorySymlink*>(f);
|
|
while(symlink != nullptr)
|
|
{
|
|
auto ent = GetEntry(symlink->name,false);
|
|
auto sym = dynamic_cast<MemorySymlink*>(f);
|
|
if(sym != nullptr)
|
|
symlink = sym;
|
|
else
|
|
{
|
|
auto myDir0 = dynamic_cast<MemoryDirectory*>(GetEntry(symlink->linkedTo.GetParent(),true));
|
|
if(myDir0 != nullptr)
|
|
{
|
|
for(auto f2 : myDir0->entries)
|
|
{
|
|
if(f2->name == symlink->linkedTo.GetFileName())
|
|
{
|
|
auto myFile = dynamic_cast<MemoryFile*>(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<MemoryFileData>();
|
|
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<MemorySymlink*>(dirent);
|
|
if(symlink != nullptr)
|
|
{
|
|
dirent = GetEntry(symlink->linkedTo,true);
|
|
}
|
|
auto dirdirent = dynamic_cast<MemoryDirectory*>(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<MemoryDirectory*>(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<MemoryDirectory*>(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<MemoryFile*>(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<MemorySymlink*>(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<MemoryDirectory*>(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<MemoryDirectory*>(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<MemoryDirectory*>(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<MemoryDirectory*>(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<MemorySymlink*>(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<size_t,std::vector<std::string>>* paths=new std::pair<size_t,std::vector<std::string>>();
|
|
paths->first=0;
|
|
mtx->Lock();
|
|
auto dir = dynamic_cast<MemoryDirectory*>(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<MemoryFile*>(GetEntry(existingFile,true));
|
|
if(existing == nullptr)
|
|
{
|
|
mtx->Unlock();
|
|
return;
|
|
}
|
|
MemoryDirectory* dir=&root;
|
|
if(newName.path.size() > 1)
|
|
{
|
|
dir = dynamic_cast<MemoryDirectory*>(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<MemorySymlink*>(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<MemoryDirectory*>(node);
|
|
|
|
auto file = dynamic_cast<MemoryFile*>(node);
|
|
|
|
auto sym = dynamic_cast<MemorySymlink*>(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<MemoryDirectory*>(node);
|
|
|
|
auto file = dynamic_cast<MemoryFile*>(node);
|
|
|
|
auto sym = dynamic_cast<MemorySymlink*>(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<Threading::Mutex>();
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
}
|