732 lines
24 KiB
C#
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();
|
|
}
|
|
}
|
|
}
|