tesses.http/Tesses.Http.VFSCollection/Class1.cs

732 lines
24 KiB
C#

using System;
using System.Linq;
using System.IO;
using System.Collections.Generic;
namespace Tesses.Http.VFSCollection
{
public static class Extensions
{
public static void ExtractTessesArchive(this VirtualStorage storage,Stream strm,bool ownStream)
{
using(TArchiveReader reader=new TArchiveReader(strm,ownStream))
{
reader.ExtractEverything((t,n,len)=>{
if(t == EntryType.File)
{
string pardir = Path.GetDirectoryName(n.TrimStart('.').TrimStart('/'));
storage.CreateDirectory(pardir);
return storage.Open(pardir,FileMode.Create,FileAccess.Write,FileShare.None);
}
if(t == EntryType.Directory)
{
storage.CreateDirectory(n.TrimStart('.').TrimStart('/'));
}
return Stream.Null;
});
}
}
}
public enum EntryType : byte
{
File=0b00000000,
Directory=0b00000001,
Symlink=0b00000010
}
public class TArchiveReader : IDisposable
{
public delegate Stream ExtractEverythingDelegate(EntryType type,string name,long entLen);
public delegate void DoneWriting(Stream strm,string name);
public delegate void Symlink(string linkPath,string linkDest);
Stream strm;
bool own;
public TArchiveReader(Stream strm,bool ownStream=false)
{
this.strm=strm;
own=ownStream;
}
public TArchiveReader(string filename)
{
this.strm = File.OpenRead(filename);
own =true;
}
private void ReadLittleEndian(byte[] arrayToFill)
{
strm.Read(arrayToFill,0,arrayToFill.Length);
if(!BitConverter.IsLittleEndian)
{
Array.Reverse(arrayToFill);
}
}
private bool ReadEntryAttributes(out EntryType type, out string name,out long len)
{
// strm.WriteByte((byte)type);
//WriteString(name);
//WriteLittleEndian(BitConverter.GetBytes(len));
int t=strm.ReadByte();
if(t == -1)
{
type=EntryType.File;
name="ENDOFSTREAM.BIN";
len=0;
return false;
}
type=(EntryType)t;
name=ReadString();
byte[] len0 = new byte[8];
ReadLittleEndian(len0);
len=BitConverter.ToInt64(len0,0);
return true;
}
private string ReadString()
{
byte[] len = new byte[4];
ReadLittleEndian(len);
byte[] strdat=new byte[BitConverter.ToInt32(len,0)];
strm.Read(strdat,0,strdat.Length);
return System.Text.Encoding.UTF8.GetString(strdat);
}
private void ReadAndDiscard(long l)
{
if(strm.CanSeek)
{
strm.Seek(l,SeekOrigin.Current);
}else{
_copyTo(Stream.Null,l);
}
}
private void _copyTo(Stream strm,long len)
{
byte[] buff=new byte[1024];
long pos=0;
int read=0;
do{
read=(int)Math.Min(1024,len-pos);
if(read == 0) return;
read=this.strm.Read(buff,0,read);
strm.Write(buff,0,read);
pos+=read;
}while(read != 0);
}
public void ExtractStartingWith(string archivePath,string outputDir,Symlink symlink=null)
{
Dictionary<string,List<string>> _f=new Dictionary<string, List<string>>();
ExtractEverything((type,name,len)=>{
if(name.StartsWith(archivePath)){
switch(type)
{
case EntryType.File:
string fname=Path.Combine(outputDir,name);
string dirname = Path.GetDirectoryName(fname);
if(!string.IsNullOrWhiteSpace(dirname))
{
Directory.CreateDirectory(dirname);
}
return File.Create(fname);
case EntryType.Directory:
string fname0=Path.Combine(outputDir,name);
Directory.CreateDirectory(fname0);
break;
}
}
return Stream.Null;
},(strm0,name)=>{strm0.Dispose();},symlink);
}
public void Extract(string archivePath,Stream strm)
{
bool hasGone=false;;
ExtractEverything((type,name,len)=>{
if(hasGone) return null;
if(type == EntryType.File && name==archivePath){
hasGone=true;
return strm;
}
return Stream.Null;
});
}
public void ExtractEverything(string outputDir,Symlink symlink=null)
{
Dictionary<string,List<string>> _f=new Dictionary<string, List<string>>();
ExtractEverything((type,name,len)=>{
switch(type)
{
case EntryType.File:
string fname=Path.Combine(outputDir,name);
string dirname = Path.GetDirectoryName(fname);
if(!string.IsNullOrWhiteSpace(dirname))
{
Directory.CreateDirectory(dirname);
}
return File.Create(fname);
case EntryType.Directory:
string fname0=Path.Combine(outputDir,name);
Directory.CreateDirectory(fname0);
break;
}
return Stream.Null;
},(strm0,name)=>{strm0.Dispose();},symlink);
}
public void ExtractEverything(ExtractEverythingDelegate del,DoneWriting doneWriting=null,Symlink symlink=null)
{
if(del == null) return;
if(hasRead && strm.CanSeek)
{
strm.Seek(0,SeekOrigin.Begin);
}
if(hasRead && !strm.CanSeek)
{
throw new Exception("Can't Seek Stream");
}else{
bool read=false;
do{
EntryType type;
string name;
long len;
read=ReadEntryAttributes(out type,out name,out len);
if(read)
{
if(type == EntryType.Symlink)
{
MemoryStream strm=new MemoryStream();
_copyTo(strm,len);
using(StreamReader rdr=new StreamReader(strm))
{
string res= rdr.ReadToEnd();
symlink?.Invoke(name,res);
}
}else{
Stream strm=del(type,name,len);
if(strm == Stream.Null)
{
if(len>0)
ReadAndDiscard(len);
}
else if(strm != null)
{
_copyTo(strm,len);
if(doneWriting != null)
{
doneWriting(strm,name);
}else{
strm.Close();
}
}else{
hasRead=true;
return;
}
}}
}while(read);
hasRead=true;
}
}
bool hasRead=false;
public void Dispose()
{
if(own) strm.Dispose();
}
}
public class MemoryStorage : VirtualStorage
{
protected override void DeleteEmptyDirectory(string dir)
{
string[] _p=dir.Split('/');
if(_p.Length < 1) return;
List<string> __c=new List<string>();
MemoryDirectory dir0=root;
foreach(var item in _p.Take(_p.Length -1))
{
dir0=_get(dir0,item) as MemoryDirectory;
__c.Add(item);
if(dir0 == null)
{
throw new Exception($"The path {PathCombine(__c)} is a file, should be a directory");
}
}
if(dir0.Entries.ContainsKey(_p[_p.Length-1]))
{
if(!(dir0.Entries[_p[_p.Length-1]].IsFile))
dir0.Entries.Remove(_p[_p.Length-1]);
}
}
public override string ActualPath => "mem:///";
public override void SetCreationTime(string filename, DateTime time)
{
string[] _p=filename.Split('/');
if(_p.Length < 1) return;
List<string> __c=new List<string>();
MemoryDirectory dir0=root;
foreach(var item in _p.Take(_p.Length -1))
{
dir0=_get(dir0,item) as MemoryDirectory;
__c.Add(item);
if(dir0 == null)
{
throw new Exception($"The path {PathCombine(__c)} is a file, should be a directory");
}
}
if(dir0.Entries.ContainsKey(_p[_p.Length-1]))
{
dir0.Entries[_p[_p.Length]].Created=time;
}
}
public override void SetLastAccessTime(string filename, DateTime time)
{
string[] _p=filename.Split('/');
if(_p.Length < 1) return;
List<string> __c=new List<string>();
MemoryDirectory dir0=root;
foreach(var item in _p.Take(_p.Length -1))
{
dir0=_get(dir0,item) as MemoryDirectory;
__c.Add(item);
if(dir0 == null)
{
throw new Exception($"The path {PathCombine(__c)} is a file, should be a directory");
}
}
if(dir0.Entries.ContainsKey(_p[_p.Length-1]))
{
dir0.Entries[_p[_p.Length]].LastAccess=time;
}
}
public override void SetLastWriteTime(string filename, DateTime time)
{
string[] _p=filename.Split('/');
if(_p.Length < 1) return;
List<string> __c=new List<string>();
MemoryDirectory dir0=root;
foreach(var item in _p.Take(_p.Length -1))
{
dir0=_get(dir0,item) as MemoryDirectory;
__c.Add(item);
if(dir0 == null)
{
throw new Exception($"The path {PathCombine(__c)} is a file, should be a directory");
}
}
if(dir0.Entries.ContainsKey(_p[_p.Length-1]))
{
dir0.Entries[_p[_p.Length]].LastWrite=time;
}
}
public override DateTime GetCreationTime(string filename)
{
string[] _p=filename.Split('/');
if(_p.Length < 1) return DateTime.Now;
List<string> __c=new List<string>();
MemoryDirectory dir0=root;
foreach(var item in _p.Take(_p.Length -1))
{
dir0=_get(dir0,item) as MemoryDirectory;
__c.Add(item);
if(dir0 == null)
{
throw new Exception($"The path {PathCombine(__c)} is a file, should be a directory");
}
}
if(dir0.Entries.ContainsKey(_p[_p.Length-1]))
{
return dir0.Entries[_p[_p.Length]].Created;
}else{
return DateTime.Now;
}
}
public override DateTime GetLastWriteTime(string filename)
{
string[] _p=filename.Split('/');
if(_p.Length < 1) return DateTime.Now;
List<string> __c=new List<string>();
MemoryDirectory dir0=root;
foreach(var item in _p.Take(_p.Length -1))
{
dir0=_get(dir0,item) as MemoryDirectory;
__c.Add(item);
if(dir0 == null)
{
throw new Exception($"The path {PathCombine(__c)} is a file, should be a directory");
}
}
if(dir0.Entries.ContainsKey(_p[_p.Length-1]))
{
return dir0.Entries[_p[_p.Length]].LastWrite;
}else{
return DateTime.Now;
}
}
public override DateTime GetLastAccessTime(string filename)
{
string[] _p=filename.Split('/');
if(_p.Length < 1) return DateTime.Now;
List<string> __c=new List<string>();
MemoryDirectory dir0=root;
foreach(var item in _p.Take(_p.Length -1))
{
dir0=_get(dir0,item) as MemoryDirectory;
__c.Add(item);
if(dir0 == null)
{
throw new Exception($"The path {PathCombine(__c)} is a file, should be a directory");
}
}
if(dir0.Entries.ContainsKey(_p[_p.Length-1]))
{
return dir0.Entries[_p[_p.Length]].LastAccess;
}else{
return DateTime.Now;
}
}
private abstract class MemoryEntry
{
public abstract bool IsFile {get;}
public DateTime Created {get;set;}
public DateTime LastWrite {get;set;}
public DateTime LastAccess {get;set;}
}
private class MemoryFile : MemoryEntry
{
public MemoryFile()
{
LastAccess=DateTime.Now;
LastWrite=DateTime.Now;
Created=DateTime.Now;
}
public bool HasWriteAccess=false;
public override bool IsFile => true;
public byte[] Data=new byte[0];
public void Overwrite(byte[] data)
{
lock(this)
{
Data=data;
LastWrite = DateTime.Now;
}
}
}
private class MemoryStream2 : MemoryStream
{
MemoryFile _f;
bool _canWrite;
public MemoryStream2(MemoryFile file,bool canWrite) : base(file.Data.ToArray(),canWrite) //copies data into file
{
file.LastAccess = DateTime.Now;
_f=file;
_canWrite=canWrite;
if(file.HasWriteAccess && _canWrite)
{
throw new IOException("MemoryStorage doesn't support simulatious writers");
}
if(!file.HasWriteAccess)
file.HasWriteAccess=_canWrite;
}
public override void Close()
{
if(_canWrite)
{
_f.Overwrite(this.ToArray());
_f.HasWriteAccess=false;
}
base.Close();
}
}
private class MemoryDirectory : MemoryEntry
{
public MemoryDirectory()
{
Created = DateTime.Now;
LastWrite=DateTime.Now;
LastAccess=DateTime.Now;
Entries=new Dictionary<string, MemoryEntry>();
}
public override bool IsFile => false;
public Dictionary<string,MemoryEntry> Entries {get;set;}
}
MemoryDirectory root=new MemoryDirectory();
public static MemoryStorage FromTessesArchive(Stream strm,bool ownStream)
{
MemoryStorage storage=new MemoryStorage();
storage.ExtractTessesArchive(strm,ownStream);
//public delegate Stream ExtractEverythingDelegate(EntryType type,string name,long entLen);
return storage;
}
private MemoryDirectory _create(MemoryDirectory dir,string p)
{
if(!dir.Entries.ContainsKey(p))
{
dir.Entries.Add(p,new MemoryDirectory());
}
var dir2= dir.Entries[p] as MemoryDirectory;
return dir2;
}
private MemoryEntry _get(MemoryDirectory dir,string p)
{
if(!dir.Entries.ContainsKey(p))
{
return null;
}
return dir.Entries[p];
}
public override void CreateDirectory(string path)
{
string[] _p=path.Split('/');
var dir = root;
List<string> __c=new List<string>();
foreach(var item in _p)
{
dir= _create(root,item);
__c.Add(item);
if(dir == null)
{
throw new Exception($"The path {PathCombine(__c)} is a file, should be a directory");
}
}
}
public override void Delete(string file)
{
string[] _p=file.Split('/');
if(_p.Length < 1) return;
List<string> __c=new List<string>();
MemoryDirectory dir=root;
foreach(var item in _p.Take(_p.Length -1))
{
dir=_get(dir,item) as MemoryDirectory;
__c.Add(item);
if(dir == null)
{
throw new Exception($"The path {PathCombine(__c)} is a file, should be a directory");
}
}
if(dir.Entries.ContainsKey(_p[_p.Length-1]))
{
if(dir.Entries[_p[_p.Length-1]].IsFile)
dir.Entries.Remove(_p[_p.Length-1]);
}
}
public override bool DirectoryExists(string filename)
{
string[] _p=filename.Split('/');
if(_p.Length < 1) return false;
List<string> __c=new List<string>();
MemoryDirectory dir=root;
foreach(var item in _p.Take(_p.Length -1))
{
dir=_get(dir,item) as MemoryDirectory;
__c.Add(item);
if(dir == null)
{
throw new Exception($"The path {PathCombine(__c)} is a file, should be a directory");
}
}
if(dir.Entries.ContainsKey(_p[_p.Length-1]))
{
return !(dir.Entries[_p[_p.Length-1]].IsFile);
}
return false;
}
public override IEnumerable<string> EnumerateDirectories(string dir)
{
string[] _p=dir.Split('/');
List<string> __c=new List<string>();
MemoryDirectory dir0=root;
foreach(var item in _p)
{
dir0=_get(dir0,item) as MemoryDirectory;
__c.Add(item);
if(dir0 == null)
{
throw new Exception($"The path {PathCombine(__c)} is a file, should be a directory");
}
}
foreach(var items in dir0.Entries)
{
if(!items.Value.IsFile)
yield return items.Key;
}
}
public override IEnumerable<string> EnumerateFiles(string dir)
{
string[] _p=dir.Split('/');
List<string> __c=new List<string>();
MemoryDirectory dir0=root;
foreach(var item in _p)
{
dir0=_get(dir0,item) as MemoryDirectory;
__c.Add(item);
if(dir0 == null)
{
throw new Exception($"The path {PathCombine(__c)} is a file, should be a directory");
}
}
foreach(var items in dir0.Entries)
{
if(items.Value.IsFile)
yield return items.Key;
}
}
public override bool FileExists(string filename)
{
string[] _p=filename.Split('/');
if(_p.Length < 1) return false;
List<string> __c=new List<string>();
MemoryDirectory dir=root;
foreach(var item in _p.Take(_p.Length -1))
{
dir=_get(dir,item) as MemoryDirectory;
__c.Add(item);
if(dir == null)
{
throw new Exception($"The path {PathCombine(__c)} is a file, should be a directory");
}
}
if(dir.Entries.ContainsKey(_p[_p.Length-1]))
{
return (dir.Entries[_p[_p.Length-1]].IsFile);
}
return false;
}
public override Stream Open(string file, FileMode mode, FileAccess access, FileShare share)
{
string[] _p=file.Split('/');
if(_p.Length < 1) throw new IOException("Path empty");
List<string> __c=new List<string>();
MemoryDirectory dir=root;
foreach(var item in _p.Take(_p.Length -1))
{
dir=_get(dir,item) as MemoryDirectory;
__c.Add(item);
if(dir == null)
{
throw new IOException($"The path {PathCombine(__c)} is a file, should be a directory");
}
}
//we will now try to get the file
string filename=_p[_p.Length -1];
if(dir.Entries.ContainsKey(filename))
{
if(!dir.Entries[filename].IsFile) throw new IOException("Not a file");
//the file exists
if(mode == FileMode.CreateNew) throw new IOException("File Exists");
var _file= dir.Entries[filename] as MemoryFile;
lock(_file)
{
if(mode == FileMode.Create) {
_file.Data=new byte[0];
}
//we need to open file
MemoryStream2 strm=new MemoryStream2(_file,access != FileAccess.Read);
if(mode != FileMode.Truncate)
{
strm.Position=0;
}else{
strm.Position=strm.Length;
}
return strm;
}
}else{
if(mode == FileMode.Open) throw new IOException("File Doesn't Exist");
MemoryFile _file=new MemoryFile();
dir.Entries.Add(filename,_file);
lock(_file)
{
MemoryStream2 strm=new MemoryStream2(_file,access != FileAccess.Read);
return strm;
}
//the file doesnt exist
}
}
}
public class SSHStorage : VirtualStorage
{
public override void CreateDirectory(string path)
{
throw new NotImplementedException();
}
public override void Delete(string file)
{
throw new NotImplementedException();
}
public override bool DirectoryExists(string filename)
{
throw new NotImplementedException();
}
public override IEnumerable<string> EnumerateDirectories(string dir)
{
throw new NotImplementedException();
}
public override IEnumerable<string> EnumerateFiles(string dir)
{
throw new NotImplementedException();
}
public override bool FileExists(string filename)
{
throw new NotImplementedException();
}
public override Stream Open(string file, FileMode mode, FileAccess access, FileShare share)
{
throw new NotImplementedException();
}
}
}