Prismo based fixes and improvements
This commit is contained in:
@@ -49,7 +49,7 @@ namespace VirtualFS.Base
|
|||||||
public BaseFileSystem(bool readOnly)
|
public BaseFileSystem(bool readOnly)
|
||||||
{
|
{
|
||||||
IsReadOnly = readOnly;
|
IsReadOnly = readOnly;
|
||||||
HighPriority = false;
|
Priority = FileSystemMountPriority.Normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Gets reference to root file system.</summary>
|
/// <summary>Gets reference to root file system.</summary>
|
||||||
@@ -57,8 +57,8 @@ namespace VirtualFS.Base
|
|||||||
|
|
||||||
/// <summary>Gets or sets a value indicating whether this file system
|
/// <summary>Gets or sets a value indicating whether this file system
|
||||||
/// has higher priority than others mounted on the same path.</summary>
|
/// has higher priority than others mounted on the same path.</summary>
|
||||||
/// <remarks>This value must be set corectly before mount.</remarks>
|
/// <remarks>This value must be set correctly before mount.</remarks>
|
||||||
public virtual bool HighPriority { get; set; }
|
public virtual FileSystemMountPriority Priority { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value indicating whether this file system is read only.
|
/// Gets a value indicating whether this file system is read only.
|
||||||
@@ -82,25 +82,42 @@ namespace VirtualFS.Base
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>Gets or sets name of file system.</summary>
|
||||||
/// Check if given path exists.
|
public virtual string Name { get; set; }
|
||||||
/// </summary>
|
|
||||||
|
/// <summary>Check if given path exists.</summary>
|
||||||
/// <param name="path">Path to check.</param>
|
/// <param name="path">Path to check.</param>
|
||||||
/// <returns>
|
/// <returns>Returns <c>true</c> if entry does
|
||||||
/// Returns <c>true</c> if entry does
|
/// exist, otherwise <c>false</c>.</returns>
|
||||||
/// exist, otherwise <c>false</c>.
|
|
||||||
/// </returns>
|
|
||||||
public abstract bool Exists(Path path);
|
public abstract bool Exists(Path path);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>Get entry located under given path.</summary>
|
||||||
/// Get entry located under given path.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">Path to get.</param>
|
/// <param name="path">Path to get.</param>
|
||||||
/// <returns>
|
/// <returns>Returns entry information.</returns>
|
||||||
/// Returns entry information.
|
|
||||||
/// </returns>
|
|
||||||
public abstract Entry GetEntry(Path path);
|
public abstract Entry GetEntry(Path path);
|
||||||
|
|
||||||
|
/// <summary>Get directory located under given path.</summary>
|
||||||
|
/// <param name="path">The path.</param>
|
||||||
|
/// <returns>Returns directory entry information.</returns>
|
||||||
|
public Directory GetDirectory(Path path)
|
||||||
|
{
|
||||||
|
if (!path.IsDirectory)
|
||||||
|
throw new InvalidOperationException(string.Format("Can't get directory from file path '{0}'", path));
|
||||||
|
|
||||||
|
return GetEntry(path) as Directory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Get file located under given path.</summary>
|
||||||
|
/// <param name="path">Path to get.</param>
|
||||||
|
/// <returns>Returns file entry information.</returns>
|
||||||
|
public File GetFile(Path path)
|
||||||
|
{
|
||||||
|
if (path.IsDirectory)
|
||||||
|
throw new InvalidOperationException(string.Format("Can't get file from directory path '{0}'", path));
|
||||||
|
|
||||||
|
return GetEntry(path) as File;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Get path to physical file containing entry.</summary>
|
/// <summary>Get path to physical file containing entry.</summary>
|
||||||
/// <param name="path">Virtual file system path.</param>
|
/// <param name="path">Virtual file system path.</param>
|
||||||
/// <returns>Real file system path or <c>null</c> if real
|
/// <returns>Real file system path or <c>null</c> if real
|
||||||
@@ -156,7 +173,7 @@ namespace VirtualFS.Base
|
|||||||
if (source == destination)
|
if (source == destination)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!destination.IsDirectory)
|
if (source.IsDirectory && !destination.IsDirectory)
|
||||||
throw new InvalidOperationException(string.Format("Can't copy to non directory path ('{0}').", destination));
|
throw new InvalidOperationException(string.Format("Can't copy to non directory path ('{0}').", destination));
|
||||||
|
|
||||||
if (!source.IsDirectory)
|
if (!source.IsDirectory)
|
||||||
@@ -209,7 +226,7 @@ namespace VirtualFS.Base
|
|||||||
|
|
||||||
private void InternalCopySingleFile(Path source, Path destination, bool overwrite, Action<Path, Path> copySuccess)
|
private void InternalCopySingleFile(Path source, Path destination, bool overwrite, Action<Path, Path> copySuccess)
|
||||||
{
|
{
|
||||||
var dst = destination.Append(source.Name);
|
var dst = destination.IsDirectory ? destination.Append(source.Name) : destination;
|
||||||
|
|
||||||
if (!Exists(source))
|
if (!Exists(source))
|
||||||
throw new InvalidOperationException("Source file doesn't exist.");
|
throw new InvalidOperationException("Source file doesn't exist.");
|
||||||
@@ -217,17 +234,17 @@ namespace VirtualFS.Base
|
|||||||
if (!overwrite && Exists(dst))
|
if (!overwrite && Exists(dst))
|
||||||
throw new InvalidOperationException("Destination file already exist.");
|
throw new InvalidOperationException("Destination file already exist.");
|
||||||
|
|
||||||
Directory dir = (Directory)GetEntry(destination);
|
Directory dir = (Directory)GetEntry(dst.Parent);
|
||||||
|
|
||||||
if (dir.IsReadOnly || dir.FileSystem.IsReadOnly)
|
if (dir.IsReadOnly || dir.FileSystem.IsReadOnly)
|
||||||
throw new InvalidOperationException("Can't copy on read only file system.");
|
throw new InvalidOperationException("Can't copy on read only file system.");
|
||||||
|
|
||||||
dir
|
dir
|
||||||
.FileCreate(source.Name)
|
.FileCreate(dst.Name)
|
||||||
.WriteAllBytes(((File)GetEntry(source)).ReadAllBytes());
|
.WriteAllBytes(((File)GetEntry(source)).ReadAllBytes());
|
||||||
|
|
||||||
if (copySuccess != null)
|
if (copySuccess != null)
|
||||||
copySuccess(source, destination.Append(source.Name));
|
copySuccess(source, dst);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Change name of the entry under specified path.</summary>
|
/// <summary>Change name of the entry under specified path.</summary>
|
||||||
@@ -255,6 +272,36 @@ namespace VirtualFS.Base
|
|||||||
/// </returns>
|
/// </returns>
|
||||||
public abstract IEnumerable<Entry> GetEntries(Path path, Regex mask = null);
|
public abstract IEnumerable<Entry> GetEntries(Path path, Regex mask = null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get directories located under given path.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">Path to get.</param>
|
||||||
|
/// <param name="mask">Mask to filter out unwanted entries.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// Returns entry information.
|
||||||
|
/// </returns>
|
||||||
|
public virtual IEnumerable<Directory> GetDirectories(Path path, Regex mask = null)
|
||||||
|
{
|
||||||
|
return GetEntries(path, mask)
|
||||||
|
.Where(e => e is Directory)
|
||||||
|
.Cast<Directory>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get files located under given path.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">Path to get.</param>
|
||||||
|
/// <param name="mask">Mask to filter out unwanted entries.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// Returns entry information.
|
||||||
|
/// </returns>
|
||||||
|
public virtual IEnumerable<File> GetFiles(Path path, Regex mask = null)
|
||||||
|
{
|
||||||
|
return GetEntries(path, mask)
|
||||||
|
.Where(e => e is File)
|
||||||
|
.Cast<File>();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create Directory and return new entry.
|
/// Create Directory and return new entry.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -305,43 +352,11 @@ namespace VirtualFS.Base
|
|||||||
throw new InvalidOperationException("Can't delete directory that contains entries.");
|
throw new InvalidOperationException("Can't delete directory that contains entries.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Opens an existing file for reading.</summary>
|
/// <summary>Creates empty file on file system and returns it's description.</summary>
|
||||||
/// <param name="path">The file to be opened for reading.</param>
|
|
||||||
/// <returns>A read-only <see cref="Stream"/> on the specified path.</returns>
|
|
||||||
public virtual Stream FileOpenRead(Path path)
|
|
||||||
{
|
|
||||||
if (path.IsDirectory)
|
|
||||||
throw new InvalidOperationException(string.Format("Can't open directory path ('{0}') as file.", path));
|
|
||||||
|
|
||||||
if (!Exists(path))
|
|
||||||
throw new InvalidOperationException("Can't open not existing file.");
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Opens an existing file for writing.</summary>
|
|
||||||
/// <param name="path">The file to be opened for writing.</param>
|
|
||||||
/// <returns>An unshared <see cref="Stream"/> object on
|
|
||||||
/// the specified path with write access.</returns>
|
|
||||||
public virtual Stream FileOpenWrite(Path path)
|
|
||||||
{
|
|
||||||
if (path.IsDirectory)
|
|
||||||
throw new InvalidOperationException(string.Format("Can't open directory path ('{0}') as file.", path));
|
|
||||||
|
|
||||||
if (IsReadOnly)
|
|
||||||
throw new InvalidOperationException(string.Format("Can't write file ('{0}') on read only file system.", path));
|
|
||||||
|
|
||||||
if (!Exists(path))
|
|
||||||
throw new InvalidOperationException("Can't open not existing file.");
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Creates or overwrites a file in the specified path.</summary>
|
|
||||||
/// <param name="path">The path and name of the file to create.</param>
|
/// <param name="path">The path and name of the file to create.</param>
|
||||||
/// <returns>A <see cref="Stream"/> that provides
|
/// <param name="overwrite">If file exists replace it.</param>
|
||||||
/// write access to the file specified in path.</returns>
|
/// <returns>File description.</returns>
|
||||||
public virtual Stream FileCreate(Path path)
|
public virtual File FileTouch(Path path, bool overwrite = false)
|
||||||
{
|
{
|
||||||
if (path.IsDirectory)
|
if (path.IsDirectory)
|
||||||
throw new InvalidOperationException(string.Format("Can't open directory path ('{0}') as file.", path));
|
throw new InvalidOperationException(string.Format("Can't open directory path ('{0}') as file.", path));
|
||||||
@@ -352,6 +367,74 @@ namespace VirtualFS.Base
|
|||||||
if (!Exists(path.Parent))
|
if (!Exists(path.Parent))
|
||||||
throw new InvalidOperationException("Can't create file in un existent directory path.");
|
throw new InvalidOperationException("Can't create file in un existent directory path.");
|
||||||
|
|
||||||
|
if (Exists(path))
|
||||||
|
{
|
||||||
|
if (overwrite)
|
||||||
|
FileDelete(path);
|
||||||
|
else
|
||||||
|
throw new InvalidOperationException("Can't create file that already exist.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Opens an existing file for reading.</summary>
|
||||||
|
/// <param name="path">The file to be opened for reading.</param>
|
||||||
|
/// <returns>A read-only <see cref="Stream"/> on the specified path.</returns>
|
||||||
|
public virtual Stream FileOpenRead(Path path)
|
||||||
|
{
|
||||||
|
if (path.IsDirectory)
|
||||||
|
throw new InvalidOperationException(string.Format("Can't open directory path ('{0}') as file.", path));
|
||||||
|
|
||||||
|
if (!Exists(path))
|
||||||
|
throw new InvalidOperationException(String.Format("Can't open not existing file '{0}'. ", path));
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Opens an existing file for writing.</summary>
|
||||||
|
/// <param name="path">The file to be opened for writing.</param>
|
||||||
|
/// <param name="append">Append file.</param>
|
||||||
|
/// <returns>An unshared <see cref="Stream"/> object on
|
||||||
|
/// the specified path with write access.</returns>
|
||||||
|
public virtual Stream FileOpenWrite(Path path, bool append = false)
|
||||||
|
{
|
||||||
|
if (path.IsDirectory)
|
||||||
|
throw new InvalidOperationException(string.Format("Can't open directory path ('{0}') as file.", path));
|
||||||
|
|
||||||
|
if (IsReadOnly)
|
||||||
|
throw new InvalidOperationException(string.Format("Can't write file ('{0}') on read only file system.", path));
|
||||||
|
|
||||||
|
if (!Exists(path))
|
||||||
|
throw new InvalidOperationException(String.Format("Can't open not existing file '{0}'. ", path));
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Creates or overwrites a file in the specified path.</summary>
|
||||||
|
/// <param name="path">The path and name of the file to create.</param>
|
||||||
|
/// <param name="overwrite">If file exists replace it.</param>
|
||||||
|
/// <returns>A <see cref="Stream"/> that provides
|
||||||
|
/// write access to the file specified in path.</returns>
|
||||||
|
public virtual Stream FileCreate(Path path, bool overwrite = false)
|
||||||
|
{
|
||||||
|
if (path.IsDirectory)
|
||||||
|
throw new InvalidOperationException(string.Format("Can't open directory path ('{0}') as file.", path));
|
||||||
|
|
||||||
|
if (IsReadOnly)
|
||||||
|
throw new InvalidOperationException(string.Format("Can't create file ('{0}') on read only file system.", path));
|
||||||
|
|
||||||
|
if (!Exists(path.Parent))
|
||||||
|
throw new InvalidOperationException("Can't create file in un existent directory path.");
|
||||||
|
|
||||||
|
if (Exists(path))
|
||||||
|
{
|
||||||
|
if (overwrite)
|
||||||
|
FileDelete(path);
|
||||||
|
else
|
||||||
|
throw new InvalidOperationException("Can't create file that already exist.");
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ namespace VirtualFS.Compressed
|
|||||||
/// <summary>Gets the Zip file.</summary>
|
/// <summary>Gets the Zip file.</summary>
|
||||||
public ZipFile File { get; private set; }
|
public ZipFile File { get; private set; }
|
||||||
|
|
||||||
private Dictionary<Path, Entry> _pathToEntry;
|
private Dictionary<Path, Entry> _pathToEntry = new Dictionary<Path, Entry>();
|
||||||
|
|
||||||
/// <summary>Gets the assembly on which file system operates.</summary>
|
/// <summary>Gets the assembly on which file system operates.</summary>
|
||||||
public Assembly Assembly { get; private set; }
|
public Assembly Assembly { get; private set; }
|
||||||
@@ -55,6 +55,12 @@ namespace VirtualFS.Compressed
|
|||||||
public ZipReadFileSystem(System.IO.Stream stream, Path root = null, Regex filter = null)
|
public ZipReadFileSystem(System.IO.Stream stream, Path root = null, Regex filter = null)
|
||||||
: base(true)
|
: base(true)
|
||||||
{
|
{
|
||||||
|
if (stream == null)
|
||||||
|
throw new ArgumentNullException("stream");
|
||||||
|
|
||||||
|
if (!stream.CanSeek)
|
||||||
|
throw new InvalidOperationException("Zip steram must be capable of seeking.");
|
||||||
|
|
||||||
File = new ZipFile(stream);
|
File = new ZipFile(stream);
|
||||||
|
|
||||||
foreach (ZipEntry zipEntry in File)
|
foreach (ZipEntry zipEntry in File)
|
||||||
@@ -91,6 +97,7 @@ namespace VirtualFS.Compressed
|
|||||||
CreationTime = zipEntry.DateTime,
|
CreationTime = zipEntry.DateTime,
|
||||||
LastAccessTime = zipEntry.DateTime,
|
LastAccessTime = zipEntry.DateTime,
|
||||||
LastWriteTime = zipEntry.DateTime,
|
LastWriteTime = zipEntry.DateTime,
|
||||||
|
Size = zipEntry.Size,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -208,7 +215,7 @@ namespace VirtualFS.Compressed
|
|||||||
{
|
{
|
||||||
base.FileOpenRead(path);
|
base.FileOpenRead(path);
|
||||||
|
|
||||||
return new VirtualStream(this, File.GetInputStream((ZipEntry)_pathToEntry[path].Data));
|
return new VirtualStream(this, File.GetInputStream((ZipEntry)_pathToEntry[path].Data), path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Performs application-defined tasks associated with
|
/// <summary>Performs application-defined tasks associated with
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ namespace VirtualFS
|
|||||||
/// <param name="forceLocal">Force usage of local file
|
/// <param name="forceLocal">Force usage of local file
|
||||||
/// system, to which directory is bound.</param>
|
/// system, to which directory is bound.</param>
|
||||||
/// <returns>Enumeration of entries in this directory.</returns>
|
/// <returns>Enumeration of entries in this directory.</returns>
|
||||||
public IEnumerable<Entry> GetEntries(Regex mask = null, bool forceLocal = false)
|
public virtual IEnumerable<Entry> GetEntries(Regex mask = null, bool forceLocal = false)
|
||||||
{
|
{
|
||||||
if (FileSystem == null)
|
if (FileSystem == null)
|
||||||
throw new InvalidOperationException("This entry has no relation with any file system.");
|
throw new InvalidOperationException("This entry has no relation with any file system.");
|
||||||
@@ -60,12 +60,119 @@ namespace VirtualFS
|
|||||||
FileSystem.GetEntries(Path, mask);
|
FileSystem.GetEntries(Path, mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Enumerate directories in this directory.</summary>
|
||||||
|
/// <param name="mask">Mask to filter out unwanted entries.</param>
|
||||||
|
/// <param name="forceLocal">Force usage of local file
|
||||||
|
/// system, to which directory is bound.</param>
|
||||||
|
/// <returns>Enumeration of entries in this directory.</returns>
|
||||||
|
public virtual IEnumerable<Directory> GetDirectories(Regex mask = null, bool forceLocal = false)
|
||||||
|
{
|
||||||
|
if (FileSystem == null)
|
||||||
|
throw new InvalidOperationException("This entry has no relation with any file system.");
|
||||||
|
|
||||||
|
// If file system is rooted, check root file system
|
||||||
|
return !forceLocal && FileSystem.RootFileSystem != null ?
|
||||||
|
FileSystem.RootFileSystem.GetDirectories(FullPath, mask) :
|
||||||
|
FileSystem.GetDirectories(Path, mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Enumerate files in this directory.</summary>
|
||||||
|
/// <param name="mask">Mask to filter out unwanted entries.</param>
|
||||||
|
/// <param name="forceLocal">Force usage of local file
|
||||||
|
/// system, to which directory is bound.</param>
|
||||||
|
/// <returns>Enumeration of entries in this directory.</returns>
|
||||||
|
public virtual IEnumerable<File> GetFiles(Regex mask = null, bool forceLocal = false)
|
||||||
|
{
|
||||||
|
if (FileSystem == null)
|
||||||
|
throw new InvalidOperationException("This entry has no relation with any file system.");
|
||||||
|
|
||||||
|
// If file system is rooted, check root file system
|
||||||
|
return !forceLocal && FileSystem.RootFileSystem != null ?
|
||||||
|
FileSystem.RootFileSystem.GetFiles(FullPath, mask) :
|
||||||
|
FileSystem.GetFiles(Path, mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Check if entry with given name exists.</summary>
|
||||||
|
/// <param name="name">Name to check.</param>
|
||||||
|
/// <param name="forceLocal">If set to <c>true</c> force usage of local file system.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if entry does exist, otherwise <c>false</c>.</returns>
|
||||||
|
/// <exception cref="System.InvalidOperationException">This entry has no relation with any file system.</exception>
|
||||||
|
public virtual bool Exists(string name, bool forceLocal = false)
|
||||||
|
{
|
||||||
|
if (FileSystem == null)
|
||||||
|
throw new InvalidOperationException("This entry has no relation with any file system.");
|
||||||
|
|
||||||
|
// If file system is rooted, check root file system
|
||||||
|
return !forceLocal && FileSystem.RootFileSystem != null ?
|
||||||
|
FileSystem.RootFileSystem.Exists(FullPath + name) :
|
||||||
|
FileSystem.Exists(Path + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Get entry located under given path.</summary>
|
||||||
|
/// <param name="name">Name of entry to get.</param>
|
||||||
|
/// <param name="forceLocal">If set to <c>true</c> force usage of local file system.</param>
|
||||||
|
/// <returns>Returns entry information.</returns>
|
||||||
|
public virtual Entry GetEntry(string name, bool forceLocal = false)
|
||||||
|
{
|
||||||
|
if (FileSystem == null)
|
||||||
|
throw new InvalidOperationException("This entry has no relation with any file system.");
|
||||||
|
|
||||||
|
// If file system is rooted, check root file system
|
||||||
|
return !forceLocal && FileSystem.RootFileSystem != null ?
|
||||||
|
FileSystem.RootFileSystem.GetEntry(FullPath + name) :
|
||||||
|
FileSystem.GetEntry(Path + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Get directory located under given path.</summary>
|
||||||
|
/// <param name="name">Name of file to get.</param>
|
||||||
|
/// <param name="forceLocal">If set to <c>true</c> force usage of local file system.</param>
|
||||||
|
/// <returns>Returns directory entry information.</returns>
|
||||||
|
public virtual Directory GetDirectory(string name, bool forceLocal = false)
|
||||||
|
{
|
||||||
|
Path path = Path + name;
|
||||||
|
|
||||||
|
if (!path.IsDirectory)
|
||||||
|
throw new InvalidOperationException(string.Format("Can't get directory from file path '{0}'", path));
|
||||||
|
|
||||||
|
return GetEntry(name, forceLocal) as Directory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Get file located under given path.</summary>
|
||||||
|
/// <param name="name">Name of file to get.</param>
|
||||||
|
/// <param name="forceLocal">If set to <c>true</c> force usage of local file system.</param>
|
||||||
|
/// <returns>Returns file entry information.</returns>
|
||||||
|
public virtual File GetFile(string name, bool forceLocal = false)
|
||||||
|
{
|
||||||
|
Path path = Path + name;
|
||||||
|
|
||||||
|
if (path.IsDirectory)
|
||||||
|
throw new InvalidOperationException(string.Format("Can't get file from directory path '{0}'", path));
|
||||||
|
|
||||||
|
return GetEntry(name, forceLocal) as File;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Get entry in this directory.</summary>
|
||||||
|
/// <param name="name">Entry name.</param>
|
||||||
|
/// <param name="forceLocal">Force usage of local file
|
||||||
|
/// system, to which directory is bound.</param>
|
||||||
|
/// <returns>Enumeration of entries in this directory.</returns>
|
||||||
|
public virtual Entry GetEntries(string name, bool forceLocal = false)
|
||||||
|
{
|
||||||
|
if (FileSystem == null)
|
||||||
|
throw new InvalidOperationException("This entry has no relation with any file system.");
|
||||||
|
|
||||||
|
// If file system is rooted, check root file system
|
||||||
|
return !forceLocal && FileSystem.RootFileSystem != null ?
|
||||||
|
FileSystem.RootFileSystem.GetEntry(FullPath + name) :
|
||||||
|
FileSystem.GetEntry(Path + name);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Create Directory and return new entry.</summary>
|
/// <summary>Create Directory and return new entry.</summary>
|
||||||
/// <param name="name">Name of directory.</param>
|
/// <param name="name">Name of directory.</param>
|
||||||
/// <param name="forceLocal">Force usage of local file
|
/// <param name="forceLocal">Force usage of local file
|
||||||
/// system, to which directory is bound.</param>
|
/// system, to which directory is bound.</param>
|
||||||
/// <returns>Created entry.</returns>
|
/// <returns>Created entry.</returns>
|
||||||
public Directory Create(string name, bool forceLocal = false)
|
public virtual Directory Create(string name, bool forceLocal = false)
|
||||||
{
|
{
|
||||||
return !forceLocal && FileSystem.RootFileSystem != null ?
|
return !forceLocal && FileSystem.RootFileSystem != null ?
|
||||||
FileSystem.RootFileSystem.DirectoryCreate(FullPath.Append(string.Format("{0}{1}",
|
FileSystem.RootFileSystem.DirectoryCreate(FullPath.Append(string.Format("{0}{1}",
|
||||||
@@ -82,7 +189,7 @@ namespace VirtualFS
|
|||||||
/// subdirectories, and files in path; otherwise <c>false</c>.</param>
|
/// subdirectories, and files in path; otherwise <c>false</c>.</param>
|
||||||
/// <param name="forceLocal">Force usage of local file
|
/// <param name="forceLocal">Force usage of local file
|
||||||
/// system, to which directory is bound.</param>
|
/// system, to which directory is bound.</param>
|
||||||
public void Delete(bool recursive = false, bool forceLocal = false)
|
public virtual void Delete(bool recursive = false, bool forceLocal = false)
|
||||||
{
|
{
|
||||||
if (!forceLocal && FileSystem.RootFileSystem != null)
|
if (!forceLocal && FileSystem.RootFileSystem != null)
|
||||||
FileSystem.RootFileSystem.DirectoryDelete(FullPath, recursive);
|
FileSystem.RootFileSystem.DirectoryDelete(FullPath, recursive);
|
||||||
@@ -96,7 +203,7 @@ namespace VirtualFS
|
|||||||
/// system, to which directory is bound.</param>
|
/// system, to which directory is bound.</param>
|
||||||
/// <remarks>Created file is empty.</remarks>
|
/// <remarks>Created file is empty.</remarks>
|
||||||
/// <returns>The <see cref="File"/> entry information.</returns>
|
/// <returns>The <see cref="File"/> entry information.</returns>
|
||||||
public File FileCreate(string name, bool forceLocal = false)
|
public virtual File FileCreate(string name, bool forceLocal = false)
|
||||||
{
|
{
|
||||||
if (!forceLocal && FileSystem.RootFileSystem != null)
|
if (!forceLocal && FileSystem.RootFileSystem != null)
|
||||||
{
|
{
|
||||||
@@ -113,7 +220,7 @@ namespace VirtualFS
|
|||||||
var path = Path.Append(
|
var path = Path.Append(
|
||||||
name.Validated("name", invalidChars: Path.InvalidFileChars, invalidSeq: Path.InvalidPathSequences));
|
name.Validated("name", invalidChars: Path.InvalidFileChars, invalidSeq: Path.InvalidPathSequences));
|
||||||
|
|
||||||
using (var stream = FileSystem.FileCreate(path))
|
using (var stream = FileSystem.FileCreate(path, true))
|
||||||
stream.Close();
|
stream.Close();
|
||||||
|
|
||||||
return FileSystem.GetEntry(path) as File;
|
return FileSystem.GetEntry(path) as File;
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ namespace VirtualFS.EmbeddedResource
|
|||||||
{
|
{
|
||||||
Assembly = assembly;
|
Assembly = assembly;
|
||||||
|
|
||||||
var fi = new System.IO.FileInfo(Assembly.Location);
|
var fi = new System.IO.FileInfo(Uri.UnescapeDataString(new UriBuilder(Assembly.CodeBase).Path));
|
||||||
|
|
||||||
_pathToResource = Assembly.GetManifestResourceNames()
|
_pathToResource = Assembly.GetManifestResourceNames()
|
||||||
.Where(x => (string.IsNullOrEmpty(rootNamespace) || x.StartsWith(rootNamespace)) && (filter == null || filter.IsMatch(x)))
|
.Where(x => (string.IsNullOrEmpty(rootNamespace) || x.StartsWith(rootNamespace)) && (filter == null || filter.IsMatch(x)))
|
||||||
@@ -68,6 +68,7 @@ namespace VirtualFS.EmbeddedResource
|
|||||||
CreationTime = fi.CreationTimeUtc,
|
CreationTime = fi.CreationTimeUtc,
|
||||||
LastAccessTime = fi.LastAccessTime,
|
LastAccessTime = fi.LastAccessTime,
|
||||||
LastWriteTime = fi.LastWriteTime,
|
LastWriteTime = fi.LastWriteTime,
|
||||||
|
Size = 0,
|
||||||
})
|
})
|
||||||
.ToDictionary(
|
.ToDictionary(
|
||||||
k => k.Path,
|
k => k.Path,
|
||||||
@@ -142,7 +143,7 @@ namespace VirtualFS.EmbeddedResource
|
|||||||
|
|
||||||
if (!ret && path.IsDirectory && _pathToResource.Keys.Any(x => x.IsParentOf(path)))
|
if (!ret && path.IsDirectory && _pathToResource.Keys.Any(x => x.IsParentOf(path)))
|
||||||
{
|
{
|
||||||
var fi = new System.IO.FileInfo(Assembly.Location);
|
var fi = new System.IO.FileInfo(Uri.UnescapeDataString(new UriBuilder(Assembly.CodeBase).Path));
|
||||||
|
|
||||||
_pathToResource.Add(path, new Directory()
|
_pathToResource.Add(path, new Directory()
|
||||||
{
|
{
|
||||||
@@ -181,7 +182,7 @@ namespace VirtualFS.EmbeddedResource
|
|||||||
/// should return null if real path can't be determined.</remarks>
|
/// should return null if real path can't be determined.</remarks>
|
||||||
public override string EntryRealPath(Path path)
|
public override string EntryRealPath(Path path)
|
||||||
{
|
{
|
||||||
return this.Assembly.Location;
|
return Uri.UnescapeDataString(new UriBuilder(Assembly.CodeBase).Path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Get entries located under given path.</summary>
|
/// <summary>Get entries located under given path.</summary>
|
||||||
@@ -209,7 +210,7 @@ namespace VirtualFS.EmbeddedResource
|
|||||||
{
|
{
|
||||||
base.FileOpenRead(path);
|
base.FileOpenRead(path);
|
||||||
|
|
||||||
return new VirtualStream(this, Assembly.GetManifestResourceStream(_pathToResource[path].Data.ToString()));
|
return new VirtualStream(this, Assembly.GetManifestResourceStream(_pathToResource[path].Data.ToString()), path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -60,6 +60,21 @@ namespace VirtualFS
|
|||||||
/// <summary>Gets or sets additional internal data for file system entry.</summary>
|
/// <summary>Gets or sets additional internal data for file system entry.</summary>
|
||||||
internal object Data { get; set; }
|
internal object Data { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Updates information about file.</summary>
|
||||||
|
public virtual void UpdateInfo()
|
||||||
|
{
|
||||||
|
var up = IsDirectory ? FileSystem.GetDirectory(Path) : FileSystem.GetEntry(Path);
|
||||||
|
|
||||||
|
if (up != null)
|
||||||
|
{
|
||||||
|
IsReadOnly = up.IsReadOnly;
|
||||||
|
IsDirectory = up.IsDirectory;
|
||||||
|
CreationTime = up.CreationTime;
|
||||||
|
LastAccessTime = up.LastAccessTime;
|
||||||
|
LastWriteTime = up.LastWriteTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether the specified <see cref="System.Object" /> is equal to this instance.
|
/// Determines whether the specified <see cref="System.Object" /> is equal to this instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ namespace VirtualFS.Extensions
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="source">The source string.</param>
|
/// <param name="source">The source string.</param>
|
||||||
/// <param name="desc">A description of the source string to build errors and exceptions if needed.</param>
|
/// <param name="desc">A description of the source string to build errors and exceptions if needed.</param>
|
||||||
/// <param name="canbeNull">True if the returned string can be null.</param>
|
/// <param name="canBeNull">True if the returned string can be null.</param>
|
||||||
/// <param name="canbeEmpty">True if the returned string can be empty.</param>
|
/// <param name="canBeEmpty">True if the returned string can be empty.</param>
|
||||||
/// <param name="trim">True to trim the returned string.</param>
|
/// <param name="trim">True to trim the returned string.</param>
|
||||||
/// <param name="trimStart">True to left-trim the returned string.</param>
|
/// <param name="trimStart">True to left-trim the returned string.</param>
|
||||||
/// <param name="trimEnd">True to right-trim the returned string.</param>
|
/// <param name="trimEnd">True to right-trim the returned string.</param>
|
||||||
@@ -53,7 +53,7 @@ namespace VirtualFS.Extensions
|
|||||||
/// <param name="invalidSeq">If not null, an array containing the sequences that are not allowed.</param>
|
/// <param name="invalidSeq">If not null, an array containing the sequences that are not allowed.</param>
|
||||||
/// <returns>A new validated string.</returns>
|
/// <returns>A new validated string.</returns>
|
||||||
public static string Validated(this string source, string desc = null,
|
public static string Validated(this string source, string desc = null,
|
||||||
bool canbeNull = false, bool canbeEmpty = false,
|
bool canBeNull = false, bool canBeEmpty = false,
|
||||||
bool trim = true, bool trimStart = false, bool trimEnd = false,
|
bool trim = true, bool trimStart = false, bool trimEnd = false,
|
||||||
int minLen = -1, int maxLen = -1, char padLeft = '\0', char padRight = '\0',
|
int minLen = -1, int maxLen = -1, char padLeft = '\0', char padRight = '\0',
|
||||||
char[] invalidChars = null, char[] validChars = null, string[] invalidSeq = null)
|
char[] invalidChars = null, char[] validChars = null, string[] invalidSeq = null)
|
||||||
@@ -64,7 +64,7 @@ namespace VirtualFS.Extensions
|
|||||||
// Validating if null sources are accepted...
|
// Validating if null sources are accepted...
|
||||||
if (source == null)
|
if (source == null)
|
||||||
{
|
{
|
||||||
if (!canbeNull) throw new ArgumentNullException(desc, string.Format("{0} cannot be null.", desc));
|
if (!canBeNull) throw new ArgumentNullException(desc, string.Format("{0} cannot be null.", desc));
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,7 +92,7 @@ namespace VirtualFS.Extensions
|
|||||||
// Validating emptyness and lenghts...
|
// Validating emptyness and lenghts...
|
||||||
if (source.Length == 0)
|
if (source.Length == 0)
|
||||||
{
|
{
|
||||||
if (!canbeEmpty) throw new ArgumentException(string.Format("{0} cannot be empty.", desc));
|
if (!canBeEmpty) throw new ArgumentException(string.Format("{0} cannot be empty.", desc));
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,9 @@ namespace VirtualFS
|
|||||||
/// <summary>File representation.</summary>
|
/// <summary>File representation.</summary>
|
||||||
public class File : Entry
|
public class File : Entry
|
||||||
{
|
{
|
||||||
|
/// <summary>Gets file size if it can be determined.</summary>
|
||||||
|
public virtual long Size { get; internal set; }
|
||||||
|
|
||||||
private static UTF8Encoding _utfNoBOM = new UTF8Encoding(false, true);
|
private static UTF8Encoding _utfNoBOM = new UTF8Encoding(false, true);
|
||||||
|
|
||||||
/// <summary>Opens an existing file for reading.</summary>
|
/// <summary>Opens an existing file for reading.</summary>
|
||||||
@@ -85,6 +88,8 @@ namespace VirtualFS
|
|||||||
using (Stream fileStream = OpenRead(true))
|
using (Stream fileStream = OpenRead(true))
|
||||||
{
|
{
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
|
if (fileStream.CanSeek)
|
||||||
|
{
|
||||||
long length = fileStream.Length;
|
long length = fileStream.Length;
|
||||||
if (length > 2147483647L)
|
if (length > 2147483647L)
|
||||||
throw new IOException("Can't read file larger than 2GB.");
|
throw new IOException("Can't read file larger than 2GB.");
|
||||||
@@ -102,6 +107,20 @@ namespace VirtualFS
|
|||||||
len -= read;
|
len -= read;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
using (MemoryStream ms = new MemoryStream())
|
||||||
|
{
|
||||||
|
byte[] buf = new byte[4096];
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
int len = fileStream.Read(buf, 0, buf.Length);
|
||||||
|
if (len == 0) break;
|
||||||
|
ms.Write(buf, 0, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
array = ms.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
@@ -147,7 +166,7 @@ namespace VirtualFS
|
|||||||
/// <param name="bytes">The bytes to write to the file.</param>
|
/// <param name="bytes">The bytes to write to the file.</param>
|
||||||
public void WriteAllBytes(byte[] bytes)
|
public void WriteAllBytes(byte[] bytes)
|
||||||
{
|
{
|
||||||
using (Stream fileStream = FileSystem.FileCreate(Path))
|
using (Stream fileStream = FileSystem.FileCreate(Path, true))
|
||||||
fileStream.Write(bytes, 0, bytes.Length);
|
fileStream.Write(bytes, 0, bytes.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,7 +175,7 @@ namespace VirtualFS
|
|||||||
/// <param name="contents">The string array to write to the file.</param>
|
/// <param name="contents">The string array to write to the file.</param>
|
||||||
public void WriteAllLines(string[] contents)
|
public void WriteAllLines(string[] contents)
|
||||||
{
|
{
|
||||||
using (Stream fileStream = FileSystem.FileCreate(Path))
|
using (Stream fileStream = FileSystem.FileCreate(Path, true))
|
||||||
using (var writer = new StreamWriter(fileStream, _utfNoBOM))
|
using (var writer = new StreamWriter(fileStream, _utfNoBOM))
|
||||||
foreach (var item in contents)
|
foreach (var item in contents)
|
||||||
writer.WriteLine(item);
|
writer.WriteLine(item);
|
||||||
@@ -167,7 +186,7 @@ namespace VirtualFS
|
|||||||
/// <param name="contents">The string to write to the file.</param>
|
/// <param name="contents">The string to write to the file.</param>
|
||||||
public void WriteAllText(string contents)
|
public void WriteAllText(string contents)
|
||||||
{
|
{
|
||||||
using (Stream fileStream = FileSystem.FileCreate(Path))
|
using (Stream fileStream = FileSystem.FileCreate(Path, true))
|
||||||
using (var writer = new StreamWriter(fileStream, _utfNoBOM))
|
using (var writer = new StreamWriter(fileStream, _utfNoBOM))
|
||||||
writer.Write(contents);
|
writer.Write(contents);
|
||||||
}
|
}
|
||||||
|
|||||||
48
VirtualFS/FileSystemMountPriority.cs
Normal file
48
VirtualFS/FileSystemMountPriority.cs
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* VirtualFS - Virtual File System library.
|
||||||
|
* Copyright (c) 2016, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||||
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace VirtualFS
|
||||||
|
{
|
||||||
|
/// <summary>Enum containing file system mount priority</summary>
|
||||||
|
public enum FileSystemMountPriority
|
||||||
|
{
|
||||||
|
/// <summary>Normal mode.</summary>
|
||||||
|
Normal,
|
||||||
|
|
||||||
|
/// <summary>Mount as last.</summary>
|
||||||
|
Low,
|
||||||
|
|
||||||
|
/// <summary>Mount as first.</summary>
|
||||||
|
High,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -43,8 +43,8 @@ namespace VirtualFS
|
|||||||
|
|
||||||
/// <summary>Gets or sets a value indicating whether this file system
|
/// <summary>Gets or sets a value indicating whether this file system
|
||||||
/// has higher priority than others mounted on the same path.</summary>
|
/// has higher priority than others mounted on the same path.</summary>
|
||||||
/// <remarks>This value must be set corectly before mount.</remarks>
|
/// <remarks>This value must be set correctly before mount.</remarks>
|
||||||
bool HighPriority { get; set; }
|
FileSystemMountPriority Priority { get; set; }
|
||||||
|
|
||||||
/// <summary>Gets a value indicating whether this file system is read only.</summary>
|
/// <summary>Gets a value indicating whether this file system is read only.</summary>
|
||||||
bool IsReadOnly { get; }
|
bool IsReadOnly { get; }
|
||||||
@@ -54,6 +54,9 @@ namespace VirtualFS
|
|||||||
/// file is open in this file system.</remarks>
|
/// file is open in this file system.</remarks>
|
||||||
bool IsBusy { get; }
|
bool IsBusy { get; }
|
||||||
|
|
||||||
|
/// <summary>Gets or sets name of file system.</summary>
|
||||||
|
string Name { get; set; }
|
||||||
|
|
||||||
#region Common
|
#region Common
|
||||||
|
|
||||||
/// <summary>Check if given path exists.</summary>
|
/// <summary>Check if given path exists.</summary>
|
||||||
@@ -67,6 +70,16 @@ namespace VirtualFS
|
|||||||
/// <returns>Returns entry information.</returns>
|
/// <returns>Returns entry information.</returns>
|
||||||
Entry GetEntry(Path path);
|
Entry GetEntry(Path path);
|
||||||
|
|
||||||
|
/// <summary>Get directory located under given path.</summary>
|
||||||
|
/// <param name="path">Path to get.</param>
|
||||||
|
/// <returns>Returns directory entry information.</returns>
|
||||||
|
Directory GetDirectory(Path path);
|
||||||
|
|
||||||
|
/// <summary>Get file located under given path.</summary>
|
||||||
|
/// <param name="path">Path to get.</param>
|
||||||
|
/// <returns>Returns file entry information.</returns>
|
||||||
|
File GetFile(Path path);
|
||||||
|
|
||||||
/// <summary>Get path to physical file containing entry.</summary>
|
/// <summary>Get path to physical file containing entry.</summary>
|
||||||
/// <remarks>This may not work with all file systems. Implementation
|
/// <remarks>This may not work with all file systems. Implementation
|
||||||
/// should return null if real path can't be determined.</remarks>
|
/// should return null if real path can't be determined.</remarks>
|
||||||
@@ -107,6 +120,18 @@ namespace VirtualFS
|
|||||||
/// <returns>Returns entry information.</returns>
|
/// <returns>Returns entry information.</returns>
|
||||||
IEnumerable<Entry> GetEntries(Path path, Regex mask = null);
|
IEnumerable<Entry> GetEntries(Path path, Regex mask = null);
|
||||||
|
|
||||||
|
/// <summary>Get directories located under given path.</summary>
|
||||||
|
/// <param name="path">Path to get.</param>
|
||||||
|
/// <param name="mask">Mask to filter out unwanted entries.</param>
|
||||||
|
/// <returns>Returns entry information.</returns>
|
||||||
|
IEnumerable<Directory> GetDirectories(Path path, Regex mask = null);
|
||||||
|
|
||||||
|
/// <summary>Get files located under given path.</summary>
|
||||||
|
/// <param name="path">Path to get.</param>
|
||||||
|
/// <param name="mask">Mask to filter out unwanted entries.</param>
|
||||||
|
/// <returns>Returns entry information.</returns>
|
||||||
|
IEnumerable<File> GetFiles(Path path, Regex mask = null);
|
||||||
|
|
||||||
/// <summary>Create directory and return new entry.</summary>
|
/// <summary>Create directory and return new entry.</summary>
|
||||||
/// <remarks>Parent directory must exist.</remarks>
|
/// <remarks>Parent directory must exist.</remarks>
|
||||||
/// <param name="path">The directory path to create.</param>
|
/// <param name="path">The directory path to create.</param>
|
||||||
@@ -122,6 +147,12 @@ namespace VirtualFS
|
|||||||
|
|
||||||
#region File
|
#region File
|
||||||
|
|
||||||
|
/// <summary>Creates empty file on file system and returns it's description.</summary>
|
||||||
|
/// <param name="path">The path and name of the file to create.</param>
|
||||||
|
/// <param name="overwrite">If file exists replace it.</param>
|
||||||
|
/// <returns>File description.</returns>
|
||||||
|
File FileTouch(Path path, bool overwrite = false);
|
||||||
|
|
||||||
/// <summary>Opens an existing file for reading.</summary>
|
/// <summary>Opens an existing file for reading.</summary>
|
||||||
/// <param name="path">The file to be opened for reading.</param>
|
/// <param name="path">The file to be opened for reading.</param>
|
||||||
/// <returns>A read-only <see cref="Stream"/> on the specified path.</returns>
|
/// <returns>A read-only <see cref="Stream"/> on the specified path.</returns>
|
||||||
@@ -129,15 +160,17 @@ namespace VirtualFS
|
|||||||
|
|
||||||
/// <summary>Opens an existing file for writing.</summary>
|
/// <summary>Opens an existing file for writing.</summary>
|
||||||
/// <param name="path">The file to be opened for writing.</param>
|
/// <param name="path">The file to be opened for writing.</param>
|
||||||
|
/// <param name="append">Append file.</param>
|
||||||
/// <returns>An unshared <see cref="Stream"/> object on
|
/// <returns>An unshared <see cref="Stream"/> object on
|
||||||
/// the specified path with write access.</returns>
|
/// the specified path with write access.</returns>
|
||||||
Stream FileOpenWrite(Path path);
|
Stream FileOpenWrite(Path path, bool append = false);
|
||||||
|
|
||||||
/// <summary>Creates or overwrites a file in the specified path.</summary>
|
/// <summary>Creates or overwrites a file in the specified path.</summary>
|
||||||
/// <param name="path">The path and name of the file to create.</param>
|
/// <param name="path">The path and name of the file to create.</param>
|
||||||
|
/// <param name="overwrite">If file exists replace it.</param>
|
||||||
/// <returns>A <see cref="Stream"/> that provides
|
/// <returns>A <see cref="Stream"/> that provides
|
||||||
/// write access to the file specified in path.</returns>
|
/// write access to the file specified in path.</returns>
|
||||||
Stream FileCreate(Path path);
|
Stream FileCreate(Path path, bool overwrite = false);
|
||||||
|
|
||||||
/// <summary>Deletes the specified file. An exception is not thrown
|
/// <summary>Deletes the specified file. An exception is not thrown
|
||||||
/// if the specified file does not exist.</summary>
|
/// if the specified file does not exist.</summary>
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ namespace VirtualFS.Implementation
|
|||||||
if (pathAndFs == null)
|
if (pathAndFs == null)
|
||||||
throw new InvalidOperationException("File system containing parent directory was not found.");
|
throw new InvalidOperationException("File system containing parent directory was not found.");
|
||||||
|
|
||||||
return pathAndFs.FileSystem.EntryRealPath(path.RemoveParent(parent));
|
return pathAndFs.FileSystem.EntryRealPath(path.RemoveParent(pathAndFs.Path));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Copies an existing file or directory to a new location.</summary>
|
/// <summary>Copies an existing file or directory to a new location.</summary>
|
||||||
@@ -215,7 +215,7 @@ namespace VirtualFS.Implementation
|
|||||||
if (pathAndFs == null)
|
if (pathAndFs == null)
|
||||||
throw new InvalidOperationException("Writable file system containing parent directory was not found.");
|
throw new InvalidOperationException("Writable file system containing parent directory was not found.");
|
||||||
|
|
||||||
pathAndFs.FileSystem.ReName(path.RemoveParent(parent), newName);
|
pathAndFs.FileSystem.ReName(path.RemoveParent(pathAndFs.Path), newName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Get entries located under given path.</summary>
|
/// <summary>Get entries located under given path.</summary>
|
||||||
@@ -235,6 +235,40 @@ namespace VirtualFS.Implementation
|
|||||||
.Distinct();
|
.Distinct();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Get directories located under given path.</summary>
|
||||||
|
/// <param name="path">Path to get.</param>
|
||||||
|
/// <param name="mask">Mask to filter out unwanted entries.</param>
|
||||||
|
/// <returns>Returns entry information.</returns>
|
||||||
|
public override IEnumerable<Directory> GetDirectories(Path path, Regex mask = null)
|
||||||
|
{
|
||||||
|
if (!path.IsDirectory)
|
||||||
|
throw new InvalidOperationException(string.Format("Path '{0}' is not a directory, thus it has no entries."));
|
||||||
|
|
||||||
|
return _mounts
|
||||||
|
.Where(x => x.Key.IsParentOfOrSame(path))
|
||||||
|
.SelectMany(v => v.Value.Where(x => x.Exists(path.RemoveParent(v.Key))),
|
||||||
|
(k, v) => v.GetDirectories(path.RemoveParent(k.Key)))
|
||||||
|
.SelectMany(e => e, (p, e) => e)
|
||||||
|
.Distinct();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Get files located under given path.</summary>
|
||||||
|
/// <param name="path">Path to get.</param>
|
||||||
|
/// <param name="mask">Mask to filter out unwanted entries.</param>
|
||||||
|
/// <returns>Returns entry information.</returns>
|
||||||
|
public override IEnumerable<File> GetFiles(Path path, Regex mask = null)
|
||||||
|
{
|
||||||
|
if (!path.IsDirectory)
|
||||||
|
throw new InvalidOperationException(string.Format("Path '{0}' is not a directory, thus it has no entries."));
|
||||||
|
|
||||||
|
return _mounts
|
||||||
|
.Where(x => x.Key.IsParentOfOrSame(path))
|
||||||
|
.SelectMany(v => v.Value.Where(x => x.Exists(path.RemoveParent(v.Key))),
|
||||||
|
(k, v) => v.GetFiles(path.RemoveParent(k.Key), mask))
|
||||||
|
.SelectMany(e => e, (p, e) => e)
|
||||||
|
.Distinct();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Create Directory and return new entry.</summary>
|
/// <summary>Create Directory and return new entry.</summary>
|
||||||
/// <param name="path">Path of directory.</param>
|
/// <param name="path">Path of directory.</param>
|
||||||
/// <returns>Created entry.</returns>
|
/// <returns>Created entry.</returns>
|
||||||
@@ -255,7 +289,7 @@ namespace VirtualFS.Implementation
|
|||||||
if (pathAndFs == null)
|
if (pathAndFs == null)
|
||||||
throw new InvalidOperationException("Writable file system containing parent directory was not found.");
|
throw new InvalidOperationException("Writable file system containing parent directory was not found.");
|
||||||
|
|
||||||
return pathAndFs.FileSystem.DirectoryCreate(path.RemoveParent(parent));
|
return pathAndFs.FileSystem.DirectoryCreate(path.RemoveParent(pathAndFs.Path));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Deletes the specified directory and, if
|
/// <summary>Deletes the specified directory and, if
|
||||||
@@ -281,7 +315,7 @@ namespace VirtualFS.Implementation
|
|||||||
if (pathAndFs == null)
|
if (pathAndFs == null)
|
||||||
throw new InvalidOperationException("Writable file system containing parent directory was not found.");
|
throw new InvalidOperationException("Writable file system containing parent directory was not found.");
|
||||||
|
|
||||||
pathAndFs.FileSystem.DirectoryDelete(path.RemoveParent(parent));
|
pathAndFs.FileSystem.DirectoryDelete(path.RemoveParent(pathAndFs.Path));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Opens an existing file for reading.</summary>
|
/// <summary>Opens an existing file for reading.</summary>
|
||||||
@@ -297,23 +331,24 @@ namespace VirtualFS.Implementation
|
|||||||
throw new InvalidOperationException(string.Format("Can't open directory path ('{0}') as file.", path));
|
throw new InvalidOperationException(string.Format("Can't open directory path ('{0}') as file.", path));
|
||||||
|
|
||||||
var pathAndFs = _mounts
|
var pathAndFs = _mounts
|
||||||
.Where(x => x.Key.IsParentOfOrSame(parent))
|
.Where(x => x.Key.IsParentOfOrSame(path))
|
||||||
.SelectMany(v => v.Value, (k, v) => new { Path = k.Key, FileSystem = v })
|
.SelectMany(v => v.Value, (k, v) => new { Path = k.Key, FileSystem = v })
|
||||||
.FirstOrDefault(pfs => pfs.FileSystem.Exists(parent.RemoveParent(pfs.Path)));
|
.FirstOrDefault(pfs => pfs.FileSystem.Exists(path.RemoveParent(pfs.Path)));
|
||||||
|
|
||||||
if (pathAndFs == null)
|
if (pathAndFs == null)
|
||||||
throw new InvalidOperationException("File system containing parent directory was not found.");
|
throw new InvalidOperationException("File system containing parent directory was not found.");
|
||||||
|
|
||||||
return pathAndFs.FileSystem.FileOpenRead(path.RemoveParent(parent));
|
return pathAndFs.FileSystem.FileOpenRead(path.RemoveParent(pathAndFs.Path));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Opens an existing file for writing.</summary>
|
/// <summary>Opens an existing file for writing.</summary>
|
||||||
/// <param name="path">The file to be opened for writing.</param>
|
/// <param name="path">The file to be opened for writing.</param>
|
||||||
|
/// <param name="append">Append file.</param>
|
||||||
/// <returns>An unshared <see cref="Stream" /> object on
|
/// <returns>An unshared <see cref="Stream" /> object on
|
||||||
/// the specified path with write access.</returns>
|
/// the specified path with write access.</returns>
|
||||||
/// <exception cref="System.InvalidOperationException">Writable
|
/// <exception cref="System.InvalidOperationException">Writable
|
||||||
/// file system containing parent directory was not found.</exception>
|
/// file system containing parent directory was not found.</exception>
|
||||||
public override Stream FileOpenWrite(Path path)
|
public override Stream FileOpenWrite(Path path, bool append = false)
|
||||||
{
|
{
|
||||||
Path parent = path.Parent;
|
Path parent = path.Parent;
|
||||||
|
|
||||||
@@ -328,16 +363,39 @@ namespace VirtualFS.Implementation
|
|||||||
if (pathAndFs == null)
|
if (pathAndFs == null)
|
||||||
throw new InvalidOperationException("Writable file system containing parent directory was not found.");
|
throw new InvalidOperationException("Writable file system containing parent directory was not found.");
|
||||||
|
|
||||||
return pathAndFs.FileSystem.FileOpenWrite(path.RemoveParent(parent));
|
return pathAndFs.FileSystem.FileOpenWrite(path.RemoveParent(pathAndFs.Path));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Creates empty file on file system and returns it's description.</summary>
|
||||||
|
/// <param name="path">The path and name of the file to create.</param>
|
||||||
|
/// <param name="overwrite">If file exists replace it.</param>
|
||||||
|
/// <returns>File description.</returns>
|
||||||
|
public override File FileTouch(Path path, bool overwrite = false)
|
||||||
|
{
|
||||||
|
Path parent = path.Parent;
|
||||||
|
|
||||||
|
if (path.IsDirectory)
|
||||||
|
throw new InvalidOperationException(string.Format("Can't open directory path ('{0}') as file.", path));
|
||||||
|
|
||||||
|
var pathAndFs = _mounts
|
||||||
|
.Where(x => x.Key.IsParentOfOrSame(parent))
|
||||||
|
.SelectMany(v => v.Value, (k, v) => new { Path = k.Key, FileSystem = v })
|
||||||
|
.FirstOrDefault(pfs => !pfs.FileSystem.IsReadOnly && pfs.FileSystem.Exists(parent.RemoveParent(pfs.Path)));
|
||||||
|
|
||||||
|
if (pathAndFs == null)
|
||||||
|
throw new InvalidOperationException("Writable file system containing parent directory was not found.");
|
||||||
|
|
||||||
|
return pathAndFs.FileSystem.FileTouch(path.RemoveParent(pathAndFs.Path), overwrite);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Creates or overwrites a file in the specified path.</summary>
|
/// <summary>Creates or overwrites a file in the specified path.</summary>
|
||||||
/// <param name="path">The path and name of the file to create.</param>
|
/// <param name="path">The path and name of the file to create.</param>
|
||||||
|
/// <param name="overwrite">If file exists replace it.</param>
|
||||||
/// <returns>A <see cref="Stream" /> that provides
|
/// <returns>A <see cref="Stream" /> that provides
|
||||||
/// write access to the file specified in path.</returns>
|
/// write access to the file specified in path.</returns>
|
||||||
/// <exception cref="System.InvalidOperationException">Writable
|
/// <exception cref="System.InvalidOperationException">Writable
|
||||||
/// file system containing parent directory was not found.</exception>
|
/// file system containing parent directory was not found.</exception>
|
||||||
public override Stream FileCreate(Path path)
|
public override Stream FileCreate(Path path, bool overwrite = false)
|
||||||
{
|
{
|
||||||
Path parent = path.Parent;
|
Path parent = path.Parent;
|
||||||
|
|
||||||
@@ -352,7 +410,7 @@ namespace VirtualFS.Implementation
|
|||||||
if (pathAndFs == null)
|
if (pathAndFs == null)
|
||||||
throw new InvalidOperationException("Writable file system containing parent directory was not found.");
|
throw new InvalidOperationException("Writable file system containing parent directory was not found.");
|
||||||
|
|
||||||
return pathAndFs.FileSystem.FileCreate(path.RemoveParent(parent));
|
return pathAndFs.FileSystem.FileCreate(path.RemoveParent(pathAndFs.Path), overwrite);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Files the delete.</summary>
|
/// <summary>Files the delete.</summary>
|
||||||
@@ -374,7 +432,7 @@ namespace VirtualFS.Implementation
|
|||||||
if (pathAndFs == null)
|
if (pathAndFs == null)
|
||||||
throw new InvalidOperationException("Writable file system containing parent directory was not found.");
|
throw new InvalidOperationException("Writable file system containing parent directory was not found.");
|
||||||
|
|
||||||
pathAndFs.FileSystem.FileDelete(path.RemoveParent(parent));
|
pathAndFs.FileSystem.FileDelete(path.RemoveParent(pathAndFs.Path));
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion IFileSystem implementation
|
#endregion IFileSystem implementation
|
||||||
@@ -403,7 +461,7 @@ namespace VirtualFS.Implementation
|
|||||||
public bool Mount(IFileSystem fs, Path path)
|
public bool Mount(IFileSystem fs, Path path)
|
||||||
{
|
{
|
||||||
if (!path.IsDirectory)
|
if (!path.IsDirectory)
|
||||||
throw new InvalidOperationException("Path '{0}' is not a directory. You can only mount file system under directories.");
|
throw new InvalidOperationException(string.Format("Path '{0}' is not a directory. You can only mount file system under directories.", path));
|
||||||
|
|
||||||
lock (_globalLock)
|
lock (_globalLock)
|
||||||
{
|
{
|
||||||
@@ -413,7 +471,15 @@ namespace VirtualFS.Implementation
|
|||||||
if (!_mounts.ContainsKey(path))
|
if (!_mounts.ContainsKey(path))
|
||||||
_mounts.Add(path, new List<IFileSystem>());
|
_mounts.Add(path, new List<IFileSystem>());
|
||||||
|
|
||||||
_mounts[path].Insert(_mounts[path].Count(f => f.HighPriority), fs);
|
if (fs.Priority == FileSystemMountPriority.High)
|
||||||
|
_mounts[path].Insert(0, fs);
|
||||||
|
else if (fs.Priority == FileSystemMountPriority.Low)
|
||||||
|
_mounts[path].Add(fs);
|
||||||
|
else
|
||||||
|
_mounts[path].Insert(_mounts[path].Count(f => f.Priority == FileSystemMountPriority.High), fs);
|
||||||
|
|
||||||
|
if (fs is BaseFileSystem)
|
||||||
|
((BaseFileSystem)fs).RootFileSystem = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -449,6 +515,9 @@ namespace VirtualFS.Implementation
|
|||||||
if (_mounts[mountPaths[i]].Count == 0)
|
if (_mounts[mountPaths[i]].Count == 0)
|
||||||
_mounts.Remove(mountPaths[i]);
|
_mounts.Remove(mountPaths[i]);
|
||||||
|
|
||||||
|
if (fs is BaseFileSystem)
|
||||||
|
((BaseFileSystem)fs).RootFileSystem = null;
|
||||||
|
|
||||||
result = true;
|
result = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using VirtualFS.Base;
|
using VirtualFS.Base;
|
||||||
|
|
||||||
@@ -36,19 +37,30 @@ namespace VirtualFS.Implementation
|
|||||||
public class VirtualStream : Stream
|
public class VirtualStream : Stream
|
||||||
{
|
{
|
||||||
/// <summary>Gets the internal stream.</summary>
|
/// <summary>Gets the internal stream.</summary>
|
||||||
|
/// <remarks>Do not close internal stream by yourself.</remarks>
|
||||||
public Stream InternalStream { get; private set; }
|
public Stream InternalStream { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>Gets the path of file.</summary>
|
||||||
|
public Path Path { get; private set; }
|
||||||
|
|
||||||
/// <summary>Gets the file system.</summary>
|
/// <summary>Gets the file system.</summary>
|
||||||
public IFileSystem FileSystem { get; private set; }
|
public IFileSystem FileSystem { get; private set; }
|
||||||
|
|
||||||
/// <summary>Initializes a new instance of
|
/// <summary>Gets or sets some unspecified kind of data.</summary>
|
||||||
/// the <see cref="VirtualStream" /> class.</summary>
|
internal object Unspecified { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Occurs when stream is closed.</summary>
|
||||||
|
public event EventHandler OnClose;
|
||||||
|
|
||||||
|
/// <summary>Initializes a new instance of the <see cref="VirtualStream"/> class.</summary>
|
||||||
/// <param name="fileSystem">The file system.</param>
|
/// <param name="fileSystem">The file system.</param>
|
||||||
/// <param name="stream">The internal stream.</param>
|
/// <param name="stream">The internal stream.</param>
|
||||||
internal VirtualStream(BaseFileSystem fileSystem, Stream stream)
|
/// <param name="path">The path of file.</param>
|
||||||
|
internal VirtualStream(BaseFileSystem fileSystem, Stream stream, Path path)
|
||||||
{
|
{
|
||||||
fileSystem.IncreaseOpened();
|
fileSystem.IncreaseOpened();
|
||||||
InternalStream = stream;
|
InternalStream = stream;
|
||||||
|
Path = path;
|
||||||
FileSystem = fileSystem;
|
FileSystem = fileSystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,6 +73,9 @@ namespace VirtualFS.Implementation
|
|||||||
{
|
{
|
||||||
((BaseFileSystem)FileSystem).DecreaseOpened();
|
((BaseFileSystem)FileSystem).DecreaseOpened();
|
||||||
InternalStream.Close();
|
InternalStream.Close();
|
||||||
|
|
||||||
|
if (OnClose != null)
|
||||||
|
OnClose(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Wrapped Properties
|
#region Wrapped Properties
|
||||||
|
|||||||
Binary file not shown.
@@ -197,31 +197,68 @@ namespace VirtualFS.Memory
|
|||||||
{
|
{
|
||||||
base.FileOpenRead(path);
|
base.FileOpenRead(path);
|
||||||
|
|
||||||
return new VirtualStream(this, new System.IO.MemoryStream((byte[])(GetEntry(path).Data ?? new byte[] { })));
|
return new VirtualStream(this, new System.IO.MemoryStream((byte[])(GetEntry(path).Data ?? new byte[] { })), path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Opens an existing file for writing.</summary>
|
/// <summary>Opens an existing file for writing.</summary>
|
||||||
/// <param name="path">The file to be opened for writing.</param>
|
/// <param name="path">The file to be opened for writing.</param>
|
||||||
|
/// <param name="append">Append file.</param>
|
||||||
/// <returns>An unshared <see cref="Stream" /> object on
|
/// <returns>An unshared <see cref="Stream" /> object on
|
||||||
/// the specified path with write access.</returns>
|
/// the specified path with write access.</returns>
|
||||||
public override System.IO.Stream FileOpenWrite(Path path)
|
public override System.IO.Stream FileOpenWrite(Path path, bool append = false)
|
||||||
{
|
{
|
||||||
base.FileOpenWrite(path);
|
base.FileOpenWrite(path);
|
||||||
|
|
||||||
var e = GetEntry(path);
|
var e = GetEntry(path);
|
||||||
var data = (byte[])(e.Data ?? new byte[] { });
|
|
||||||
|
|
||||||
var ms = new System.IO.MemoryStream();
|
var ms = new System.IO.MemoryStream();
|
||||||
ms.Write(data, 0, data.Length);
|
|
||||||
|
|
||||||
return new MemoryWriteVirtualStream(this, (File)e, ms);
|
if (append)
|
||||||
|
{
|
||||||
|
var data = (byte[])(e.Data ?? new byte[] { });
|
||||||
|
ms.Write(data, 0, data.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new MemoryWriteVirtualStream(this, (File)e, path, ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Creates empty file on file system and returns it's description.</summary>
|
||||||
|
/// <param name="path">The path and name of the file to create.</param>
|
||||||
|
/// <param name="overwrite">If file exists replace it.</param>
|
||||||
|
/// <returns>File description.</returns>
|
||||||
|
public override File FileTouch(Path path, bool overwrite = false)
|
||||||
|
{
|
||||||
|
base.FileTouch(path, overwrite);
|
||||||
|
|
||||||
|
var e = (File)GetEntry(path);
|
||||||
|
if (e != null)
|
||||||
|
return e;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
e = new File
|
||||||
|
{
|
||||||
|
Path = path,
|
||||||
|
Data = new byte[] { },
|
||||||
|
FileSystem = this,
|
||||||
|
IsReadOnly = IsReadOnly,
|
||||||
|
CreationTime = DateTime.Now,
|
||||||
|
LastAccessTime = DateTime.Now,
|
||||||
|
LastWriteTime = DateTime.Now,
|
||||||
|
Size = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
lock (_lock)
|
||||||
|
_pathToEntry[path] = e;
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Creates or overwrites a file in the specified path.</summary>
|
/// <summary>Creates or overwrites a file in the specified path.</summary>
|
||||||
/// <param name="path">The path and name of the file to create.</param>
|
/// <param name="path">The path and name of the file to create.</param>
|
||||||
|
/// <param name="overwrite">If file exists replace it.</param>
|
||||||
/// <returns>A <see cref="Stream" /> that provides
|
/// <returns>A <see cref="Stream" /> that provides
|
||||||
/// write access to the file specified in path.</returns>
|
/// write access to the file specified in path.</returns>
|
||||||
public override System.IO.Stream FileCreate(Path path)
|
public override System.IO.Stream FileCreate(Path path, bool overwrite = false)
|
||||||
{
|
{
|
||||||
base.FileCreate(path);
|
base.FileCreate(path);
|
||||||
|
|
||||||
@@ -239,13 +276,14 @@ namespace VirtualFS.Memory
|
|||||||
CreationTime = DateTime.Now,
|
CreationTime = DateTime.Now,
|
||||||
LastAccessTime = DateTime.Now,
|
LastAccessTime = DateTime.Now,
|
||||||
LastWriteTime = DateTime.Now,
|
LastWriteTime = DateTime.Now,
|
||||||
|
Size = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
_pathToEntry[path] = e;
|
_pathToEntry[path] = e;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new MemoryWriteVirtualStream(this, e, new System.IO.MemoryStream());
|
return new MemoryWriteVirtualStream(this, e, path, new System.IO.MemoryStream());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Deletes the specified file. An exception is not thrown
|
/// <summary>Deletes the specified file. An exception is not thrown
|
||||||
|
|||||||
@@ -44,9 +44,10 @@ namespace VirtualFS.Memory
|
|||||||
/// <see cref="MemoryWriteVirtualStream" /> class.</summary>
|
/// <see cref="MemoryWriteVirtualStream" /> class.</summary>
|
||||||
/// <param name="fileSystem">The file system.</param>
|
/// <param name="fileSystem">The file system.</param>
|
||||||
/// <param name="file">The file to which stream belongs.</param>
|
/// <param name="file">The file to which stream belongs.</param>
|
||||||
|
/// <param name="path">The path of file.</param>
|
||||||
/// <param name="stream">The internal stream.</param>
|
/// <param name="stream">The internal stream.</param>
|
||||||
internal MemoryWriteVirtualStream(BaseFileSystem fileSystem, File file, MemoryStream stream)
|
internal MemoryWriteVirtualStream(BaseFileSystem fileSystem, File file, Path path, MemoryStream stream)
|
||||||
: base(fileSystem, stream)
|
: base(fileSystem, stream, path)
|
||||||
{
|
{
|
||||||
File = file;
|
File = file;
|
||||||
}
|
}
|
||||||
@@ -59,6 +60,7 @@ namespace VirtualFS.Memory
|
|||||||
public override void Close()
|
public override void Close()
|
||||||
{
|
{
|
||||||
File.Data = ((MemoryStream)InternalStream).ToArray();
|
File.Data = ((MemoryStream)InternalStream).ToArray();
|
||||||
|
File.Size = ((byte[])File.Data).LongLength;
|
||||||
base.Close();
|
base.Close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,17 +27,15 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using VirtualFS.Extensions;
|
using VirtualFS.Extensions;
|
||||||
|
|
||||||
namespace VirtualFS
|
namespace VirtualFS
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>Class representing path in file system.</summary>
|
||||||
/// Class representing path in file system.
|
/// <remarks>Path is always rooted, so you can't make a mistake.</remarks>
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Path is always rooted, so you can't make a mistake.
|
|
||||||
/// </remarks>
|
|
||||||
public class Path
|
public class Path
|
||||||
{
|
{
|
||||||
#region Static part
|
#region Static part
|
||||||
@@ -76,6 +74,40 @@ namespace VirtualFS
|
|||||||
Root = new Path();
|
Root = new Path();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Determines whether the specified path is rooted.</summary>
|
||||||
|
/// <param name="path">The path to check.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if the specified path is rooted; otherwise, <c>false</c>.</returns>
|
||||||
|
public static bool IsRooted(string path)
|
||||||
|
{
|
||||||
|
return path[0] == SeparatorChar;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Creates the rooted path.</summary>
|
||||||
|
/// <param name="path">The path.</param>
|
||||||
|
/// <returns>Path that is rooted in case of passing not rooted path in form of string.</returns>
|
||||||
|
public static Path CreateRootedPath(string path)
|
||||||
|
{
|
||||||
|
return IsRooted(path) ? (Path)path : Root + path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Tries the parse path.</summary>
|
||||||
|
/// <param name="path">The path.</param>
|
||||||
|
/// <param name="vfspath">The virtual file system path.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if the specified path was parsed; otherwise, <c>false</c>.</returns>
|
||||||
|
public static bool TryParsePath(string path, out Path vfspath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
vfspath = Path.CreateRootedPath(path);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
vfspath = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return vfspath != null;
|
||||||
|
}
|
||||||
|
|
||||||
#endregion Static part
|
#endregion Static part
|
||||||
|
|
||||||
private string _path;
|
private string _path;
|
||||||
@@ -89,20 +121,41 @@ namespace VirtualFS
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>Initializes a new instance of the <see cref="Path"/> class.</summary>
|
||||||
/// Initializes a new instance of the <see cref="Path"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">The path.</param>
|
/// <param name="path">The path.</param>
|
||||||
/// <exception cref="System.InvalidOperationException">Path is not rooted.</exception>
|
/// <exception cref="System.InvalidOperationException">Path is not rooted.</exception>
|
||||||
public Path(string path)
|
public Path(string path)
|
||||||
{
|
{
|
||||||
_path = path.Validated("Path", invalidChars: InvalidPathChars, invalidSeq: InvalidPathSequences);
|
|
||||||
_segments = _path.Split(SeparatorChar).Where(x => !string.IsNullOrEmpty(x)).ToArray();
|
|
||||||
|
|
||||||
if (path[0] != SeparatorChar)
|
if (path[0] != SeparatorChar)
|
||||||
throw new InvalidOperationException("Path is not rooted.");
|
throw new InvalidOperationException("Path is not rooted.");
|
||||||
|
|
||||||
IsDirectory = _path.EndsWith(SeparatorChar.ToString());
|
List<string> segments = path.Split(SeparatorChar).Where(x => !string.IsNullOrEmpty(x)).ToList();
|
||||||
|
List<string> finalSegments = new List<string>();
|
||||||
|
|
||||||
|
foreach (string segment in segments)
|
||||||
|
{
|
||||||
|
if (segment == "." || segment == "")
|
||||||
|
continue;
|
||||||
|
else if (segment == "..")
|
||||||
|
{
|
||||||
|
if (finalSegments.Any())
|
||||||
|
finalSegments.RemoveAt(finalSegments.Count - 1);
|
||||||
|
else
|
||||||
|
throw new InvalidOperationException("Path tries to leave root.");
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
//else if (segment.ToArray().Distinct().FirstOrDefault() == '.')
|
||||||
|
// throw new InvalidOperationException("Path contains too much M&M's.");
|
||||||
|
|
||||||
|
finalSegments.Add(segment);
|
||||||
|
}
|
||||||
|
|
||||||
|
IsDirectory = path.EndsWith(SeparatorChar.ToString());
|
||||||
|
|
||||||
|
_path = string.Concat(SeparatorChar.ToString(), string.Join(SeparatorChar.ToString(), finalSegments), IsDirectory && finalSegments.Any() ? SeparatorChar.ToString() : string.Empty)
|
||||||
|
.Validated("Path", invalidChars: InvalidPathChars, invalidSeq: InvalidPathSequences);
|
||||||
|
_segments = finalSegments.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Gets a value indicating whether this instance is directory.</summary>
|
/// <summary>Gets a value indicating whether this instance is directory.</summary>
|
||||||
@@ -166,6 +219,22 @@ namespace VirtualFS
|
|||||||
return name.Substring(extensionIndex + 1);
|
return name.Substring(extensionIndex + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Gets the file name without extension.</summary>
|
||||||
|
/// <returns>Return file name without extension as string.</returns>
|
||||||
|
/// <exception cref="System.ArgumentException">The specified path is not a file.</exception>
|
||||||
|
public string GetNameWithoutExtension()
|
||||||
|
{
|
||||||
|
if (IsDirectory)
|
||||||
|
throw new ArgumentException("The specified path is not a file.");
|
||||||
|
|
||||||
|
string name = _segments.Last();
|
||||||
|
int extensionIndex = name.LastIndexOf(ExtensionChar);
|
||||||
|
if (extensionIndex < 0 || extensionIndex == name.Length)
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
return name.Substring(0, extensionIndex);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Gets the name of entry from path.</summary>
|
/// <summary>Gets the name of entry from path.</summary>
|
||||||
public string Name { get { return _segments.Length > 0 ? _segments.Last() : null; } }
|
public string Name { get { return _segments.Length > 0 ? _segments.Last() : null; } }
|
||||||
|
|
||||||
@@ -203,7 +272,7 @@ namespace VirtualFS
|
|||||||
{
|
{
|
||||||
if (!parent.IsDirectory)
|
if (!parent.IsDirectory)
|
||||||
throw new ArgumentException("The specified path can not be the parent of this path: it is not a directory.");
|
throw new ArgumentException("The specified path can not be the parent of this path: it is not a directory.");
|
||||||
if (!_path.StartsWith(_path))
|
if (!_path.StartsWith(parent))
|
||||||
throw new ArgumentException("The specified path is not a parent of this path.");
|
throw new ArgumentException("The specified path is not a parent of this path.");
|
||||||
|
|
||||||
return new Path(_path.Remove(0, parent._path.Length - 1));
|
return new Path(_path.Remove(0, parent._path.Length - 1));
|
||||||
@@ -211,7 +280,26 @@ namespace VirtualFS
|
|||||||
|
|
||||||
internal Path AddParent(Path parent)
|
internal Path AddParent(Path parent)
|
||||||
{
|
{
|
||||||
return parent.ToString().TrimEnd(SeparatorChar) + this;
|
return string.Concat(parent.ToString().TrimEnd(SeparatorChar), this.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Combine paths.</summary>
|
||||||
|
/// <param name="components">Array of paths to combine.</param>
|
||||||
|
/// <returns>Instance of Path</returns>
|
||||||
|
public static Path Combine(params string[] components)
|
||||||
|
{
|
||||||
|
var list = new List<string>();
|
||||||
|
foreach (var component in components)
|
||||||
|
if (!string.IsNullOrEmpty(component))
|
||||||
|
foreach (var c in component.Split(new char[] { SeparatorChar }, StringSplitOptions.RemoveEmptyEntries))
|
||||||
|
list.Add(c);
|
||||||
|
|
||||||
|
var last = components.Where(x => !string.IsNullOrEmpty(x)).LastOrDefault();
|
||||||
|
if (last == null)
|
||||||
|
last = string.Empty;
|
||||||
|
|
||||||
|
return (string.Join(SeparatorChar.ToString(), list.ToArray()) +
|
||||||
|
(last.EndsWith(SeparatorChar.ToString()) ? SeparatorChar.ToString() : string.Empty));
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Operators
|
#region Operators
|
||||||
@@ -220,14 +308,14 @@ namespace VirtualFS
|
|||||||
/// <param name="path">The path.</param>
|
/// <param name="path">The path.</param>
|
||||||
public static implicit operator string(Path path)
|
public static implicit operator string(Path path)
|
||||||
{
|
{
|
||||||
return path._path;
|
return path == null ? null : path._path;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Convert string to path.</summary>
|
/// <summary>Convert string to path.</summary>
|
||||||
/// <param name="path">The path.</param>
|
/// <param name="path">The path.</param>
|
||||||
public static implicit operator Path(string path)
|
public static implicit operator Path(string path)
|
||||||
{
|
{
|
||||||
return new Path(path);
|
return path == null ? null : new Path(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Determines whether the specified <see cref="System.Object" /> is equal to this instance.</summary>
|
/// <summary>Determines whether the specified <see cref="System.Object" /> is equal to this instance.</summary>
|
||||||
|
|||||||
378
VirtualFS/Physical/FtpFileSystem.cs
Normal file
378
VirtualFS/Physical/FtpFileSystem.cs
Normal file
@@ -0,0 +1,378 @@
|
|||||||
|
/*
|
||||||
|
* VirtualFS - Virtual File System library.
|
||||||
|
* Copyright (c) 2013, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||||
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.FtpClient;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using VirtualFS.Base;
|
||||||
|
using VirtualFS.Implementation;
|
||||||
|
|
||||||
|
namespace VirtualFS.Physical
|
||||||
|
{
|
||||||
|
/// <summary>Ftp file system implementation.</summary>
|
||||||
|
/// <remarks>This is very, very simple implementation
|
||||||
|
/// of a FTP file system. Use this when regular FTP won't do the job.</remarks>
|
||||||
|
public class FtpFileSystem : BaseFileSystem
|
||||||
|
{
|
||||||
|
private string _ftpServer;
|
||||||
|
private int _ftpServerPort;
|
||||||
|
private string _userName;
|
||||||
|
private string _password;
|
||||||
|
private string _rootPath;
|
||||||
|
|
||||||
|
/// <summary>Gets or sets the connection type, default is
|
||||||
|
/// AutoPassive which tries
|
||||||
|
/// a connection with EPSV first and if it fails then tries
|
||||||
|
/// PASV before giving up. If you know exactly which kind of
|
||||||
|
/// connection you need you can slightly increase performance
|
||||||
|
/// by defining a speicific type of passive or active data
|
||||||
|
/// connection here.</summary>
|
||||||
|
public FtpDataConnectionType ConnectionType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Initializes a new instance of the <see cref="FtpFileSystem"/> class.</summary>
|
||||||
|
/// <param name="ftpServer">The FTP server.</param>
|
||||||
|
/// <param name="userName">FTP User name.</param>
|
||||||
|
/// <param name="password">FTP password.</param>
|
||||||
|
/// <param name="rootPath">FTP root path on server.</param>
|
||||||
|
public FtpFileSystem(Uri ftpServer, string userName, string password, string rootPath)
|
||||||
|
: base(false)
|
||||||
|
{
|
||||||
|
ConnectionType = FtpDataConnectionType.AutoActive;
|
||||||
|
_ftpServer = ftpServer.Host;
|
||||||
|
_ftpServerPort = ftpServer.Port;
|
||||||
|
_userName = userName;
|
||||||
|
_password = password;
|
||||||
|
_rootPath = rootPath;
|
||||||
|
|
||||||
|
if (_ftpServer.EndsWith("/"))
|
||||||
|
_ftpServer = _ftpServer.Substring(0, _ftpServer.Length - 1);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(_rootPath) && _rootPath.EndsWith("/"))
|
||||||
|
_rootPath = _rootPath.Substring(0, _rootPath.Length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Check if given path exists.</summary>
|
||||||
|
/// <param name="path">Path to check.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if entry does
|
||||||
|
/// exist, otherwise <c>false</c>.</returns>
|
||||||
|
public override bool Exists(Path path)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (FtpClient conn = GetConnection())
|
||||||
|
{
|
||||||
|
if (path.IsDirectory)
|
||||||
|
return conn.DirectoryExists(GetFtpPath(path));
|
||||||
|
else
|
||||||
|
return conn.FileExists(GetFtpPath(path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (WebException)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Get entry located under given path.</summary>
|
||||||
|
/// <param name="path">Path to get.</param>
|
||||||
|
/// <returns>Returns entry information.</returns>
|
||||||
|
public override Entry GetEntry(Path path)
|
||||||
|
{
|
||||||
|
Entry result = null;
|
||||||
|
|
||||||
|
if (Exists(path))
|
||||||
|
{
|
||||||
|
if (path.IsDirectory)
|
||||||
|
result = new Directory();
|
||||||
|
else
|
||||||
|
result = new File();
|
||||||
|
|
||||||
|
result.IsReadOnly = IsReadOnly;
|
||||||
|
result.Path = path;
|
||||||
|
result.FileSystem = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Get path to physical file containing entry.</summary>
|
||||||
|
/// <param name="path">Virtual file system path.</param>
|
||||||
|
/// <returns>Real file system path or <c>null</c> if real
|
||||||
|
/// path doesn't exist.</returns>
|
||||||
|
/// <remarks>This may not work with all file systems. Implementation
|
||||||
|
/// should return null if real path can't be determined.</remarks>
|
||||||
|
public override string EntryRealPath(Path path)
|
||||||
|
{
|
||||||
|
string uri = _ftpServer;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(_rootPath))
|
||||||
|
uri += _rootPath;
|
||||||
|
|
||||||
|
if (path.Parent != null)
|
||||||
|
uri += (string)path.Parent + (string)path.Name ?? string.Empty;
|
||||||
|
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Get entries located under given path.</summary>
|
||||||
|
/// <param name="path">Path to get.</param>
|
||||||
|
/// <param name="mask">Mask to filter out unwanted entries.</param>
|
||||||
|
/// <returns>Returns entry information.</returns>
|
||||||
|
public override IEnumerable<Entry> GetEntries(Path path, Regex mask = null)
|
||||||
|
{
|
||||||
|
if (!path.IsDirectory)
|
||||||
|
throw new InvalidOperationException(string.Format("Path '{0}' is not a directory, thus it has no entries."));
|
||||||
|
|
||||||
|
List<Entry> result = new List<Entry>();
|
||||||
|
|
||||||
|
using (FtpClient conn = GetConnection())
|
||||||
|
{
|
||||||
|
foreach (FtpListItem item in conn
|
||||||
|
.GetListing(GetFtpPath(path),
|
||||||
|
FtpListOption.Modify | FtpListOption.Size | FtpListOption.DerefLinks | FtpListOption.AllFiles))
|
||||||
|
{
|
||||||
|
switch (item.Type)
|
||||||
|
{
|
||||||
|
case FtpFileSystemObjectType.File:
|
||||||
|
result.Add(new File()
|
||||||
|
{
|
||||||
|
IsReadOnly = IsReadOnly,
|
||||||
|
Path = path + item.Name,
|
||||||
|
FileSystem = this,
|
||||||
|
CreationTime = item.Created,
|
||||||
|
LastWriteTime = item.Modified,
|
||||||
|
LastAccessTime = item.Modified,
|
||||||
|
Size = item.Size,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FtpFileSystemObjectType.Directory:
|
||||||
|
result.Add(new Directory()
|
||||||
|
{
|
||||||
|
IsReadOnly = IsReadOnly,
|
||||||
|
Path = string.Concat(path, item.Name.TrimEnd('/'), "/"),
|
||||||
|
FileSystem = this,
|
||||||
|
CreationTime = item.Created,
|
||||||
|
LastWriteTime = item.Modified,
|
||||||
|
LastAccessTime = item.Modified,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FtpFileSystemObjectType.Link:
|
||||||
|
|
||||||
|
if (item.LinkObject != null)
|
||||||
|
{
|
||||||
|
switch (item.LinkObject.Type)
|
||||||
|
{
|
||||||
|
case FtpFileSystemObjectType.File:
|
||||||
|
result.Add(new File()
|
||||||
|
{
|
||||||
|
IsReadOnly = IsReadOnly,
|
||||||
|
Path = path + item.Name,
|
||||||
|
FileSystem = this,
|
||||||
|
CreationTime = item.LinkObject.Created,
|
||||||
|
LastWriteTime = item.LinkObject.Modified,
|
||||||
|
LastAccessTime = item.LinkObject.Modified,
|
||||||
|
Size = item.LinkObject.Size,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FtpFileSystemObjectType.Directory:
|
||||||
|
result.Add(new Directory()
|
||||||
|
{
|
||||||
|
IsReadOnly = IsReadOnly,
|
||||||
|
Path = string.Concat(path, item.Name.TrimEnd('/'), "/"),
|
||||||
|
FileSystem = this,
|
||||||
|
CreationTime = item.LinkObject.Created,
|
||||||
|
LastWriteTime = item.LinkObject.Modified,
|
||||||
|
LastAccessTime = item.LinkObject.Modified,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Change name of the entry under specified path.</summary>
|
||||||
|
/// <param name="path">The path to entry which name will be changed.</param>
|
||||||
|
/// <param name="newName">The new name of entry.</param>
|
||||||
|
public override void ReName(Path path, string newName)
|
||||||
|
{
|
||||||
|
base.ReName(path, newName);
|
||||||
|
|
||||||
|
using (FtpClient conn = GetConnection())
|
||||||
|
conn.Rename(GetFtpPath(path), GetFtpPath(path.Parent + newName));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Create Directory and return new entry.</summary>
|
||||||
|
/// <param name="path">Path of directory.</param>
|
||||||
|
/// <returns>Created entry.</returns>
|
||||||
|
public override Directory DirectoryCreate(Path path)
|
||||||
|
{
|
||||||
|
base.DirectoryCreate(path);
|
||||||
|
|
||||||
|
using (FtpClient conn = GetConnection())
|
||||||
|
conn.CreateDirectory(GetFtpPath(path), true);
|
||||||
|
|
||||||
|
return (Directory)GetEntry(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Deletes the specified directory and, if
|
||||||
|
/// indicated, any subdirectories in the directory.</summary>
|
||||||
|
/// <param name="path">The path of the directory to remove.</param>
|
||||||
|
/// <param name="recursive">Set <c>true</c> to remove directories,
|
||||||
|
/// subdirectories, and files in path; otherwise <c>false</c>.</param>
|
||||||
|
public override void DirectoryDelete(Path path, bool recursive = false)
|
||||||
|
{
|
||||||
|
base.DirectoryDelete(path, recursive);
|
||||||
|
|
||||||
|
using (FtpClient conn = GetConnection())
|
||||||
|
conn.DeleteDirectory(GetFtpPath(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Opens an existing file for reading.</summary>
|
||||||
|
/// <param name="path">The file to be opened for reading.</param>
|
||||||
|
/// <returns>A read-only <see cref="Stream" /> on the specified path.</returns>
|
||||||
|
public override Stream FileOpenRead(Path path)
|
||||||
|
{
|
||||||
|
base.FileOpenRead(path);
|
||||||
|
|
||||||
|
FtpClient conn = GetConnection();
|
||||||
|
|
||||||
|
var ret = new VirtualStream(this, conn.OpenRead(GetFtpPath(path)), path);
|
||||||
|
|
||||||
|
ret.Unspecified = conn;
|
||||||
|
ret.OnClose += (s, e) => ((FtpClient)((VirtualStream)s).Unspecified).Dispose();
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Opens an existing file for writing.</summary>
|
||||||
|
/// <param name="path">The file to be opened for writing.</param>
|
||||||
|
/// <param name="append">Append file.</param>
|
||||||
|
/// <returns>An unshared <see cref="Stream" /> object on
|
||||||
|
/// the specified path with write access.</returns>
|
||||||
|
public override Stream FileOpenWrite(Path path, bool append = false)
|
||||||
|
{
|
||||||
|
base.FileOpenWrite(path, append);
|
||||||
|
|
||||||
|
return FileOpenWritePrivate(path, append);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Creates empty file on file system and returns it's description.</summary>
|
||||||
|
/// <param name="path">The path and name of the file to create.</param>
|
||||||
|
/// <param name="overwrite">If file exists replace it.</param>
|
||||||
|
/// <returns>File description.</returns>
|
||||||
|
public override File FileTouch(Path path, bool overwrite = false)
|
||||||
|
{
|
||||||
|
base.FileTouch(path, overwrite);
|
||||||
|
using (var fs = FileOpenWritePrivate(path, false))
|
||||||
|
fs.Close();
|
||||||
|
|
||||||
|
return new File()
|
||||||
|
{
|
||||||
|
IsReadOnly = IsReadOnly,
|
||||||
|
Path = path,
|
||||||
|
FileSystem = this,
|
||||||
|
CreationTime = DateTime.Now,
|
||||||
|
LastWriteTime = DateTime.Now,
|
||||||
|
LastAccessTime = DateTime.Now,
|
||||||
|
Size = 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Creates or overwrites a file in the specified path.</summary>
|
||||||
|
/// <param name="path">The path and name of the file to create.</param>
|
||||||
|
/// <param name="overwrite">If file exists replace it.</param>
|
||||||
|
/// <returns>A <see cref="Stream" /> that provides
|
||||||
|
/// write access to the file specified in path.</returns>
|
||||||
|
public override Stream FileCreate(Path path, bool overwrite = false)
|
||||||
|
{
|
||||||
|
base.FileCreate(path, overwrite);
|
||||||
|
|
||||||
|
return FileOpenWritePrivate(path, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Deletes the specified file. An exception is not thrown
|
||||||
|
/// if the specified file does not exist.</summary>
|
||||||
|
/// <param name="path">The path of the file to be deleted.</param>
|
||||||
|
public override void FileDelete(Path path)
|
||||||
|
{
|
||||||
|
base.FileDelete(path);
|
||||||
|
|
||||||
|
using (FtpClient conn = GetConnection())
|
||||||
|
conn.DeleteFile(GetFtpPath(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Stream FileOpenWritePrivate(Path path, bool append)
|
||||||
|
{
|
||||||
|
FtpClient conn = GetConnection();
|
||||||
|
|
||||||
|
var ret = new VirtualStream(this, append ? conn.OpenAppend(GetFtpPath(path), FtpDataType.Binary) : conn.OpenWrite(GetFtpPath(path), FtpDataType.Binary), path);
|
||||||
|
|
||||||
|
ret.Unspecified = conn;
|
||||||
|
ret.OnClose += (s, e) => ((FtpClient)((VirtualStream)s).Unspecified).Dispose();
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetFtpPath(string path)
|
||||||
|
{
|
||||||
|
string fullPath = string.Empty; // _ftpServer;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(_rootPath))
|
||||||
|
fullPath += _rootPath;
|
||||||
|
|
||||||
|
if (path != null)
|
||||||
|
fullPath += path;
|
||||||
|
|
||||||
|
return fullPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private FtpClient GetConnection()
|
||||||
|
{
|
||||||
|
FtpClient conn = new FtpClient();
|
||||||
|
conn.DataConnectionType = ConnectionType;
|
||||||
|
conn.Host = _ftpServer;
|
||||||
|
conn.Port = _ftpServerPort;
|
||||||
|
|
||||||
|
conn.Credentials = new NetworkCredential(_userName, _password);
|
||||||
|
|
||||||
|
return conn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -62,7 +62,7 @@ namespace VirtualFS.Physical
|
|||||||
return new Directory
|
return new Directory
|
||||||
{
|
{
|
||||||
IsReadOnly = IsReadOnly,
|
IsReadOnly = IsReadOnly,
|
||||||
Path = rootPath + string.Format("{0}{1}", fsi.Name, Path.SeparatorChar),
|
Path = rootPath == null ? Path.Root : (Path)(rootPath + string.Format("{0}{1}", fsi.Name, Path.SeparatorChar)),
|
||||||
FileSystem = this,
|
FileSystem = this,
|
||||||
CreationTime = fsi.CreationTimeUtc,
|
CreationTime = fsi.CreationTimeUtc,
|
||||||
LastAccessTime = fsi.LastAccessTime,
|
LastAccessTime = fsi.LastAccessTime,
|
||||||
@@ -77,6 +77,7 @@ namespace VirtualFS.Physical
|
|||||||
CreationTime = fsi.CreationTimeUtc,
|
CreationTime = fsi.CreationTimeUtc,
|
||||||
LastAccessTime = fsi.LastAccessTime,
|
LastAccessTime = fsi.LastAccessTime,
|
||||||
LastWriteTime = fsi.LastWriteTime,
|
LastWriteTime = fsi.LastWriteTime,
|
||||||
|
Size = (fsi as System.IO.FileInfo).Length,
|
||||||
};
|
};
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -108,7 +109,7 @@ namespace VirtualFS.Physical
|
|||||||
fsi = new System.IO.FileInfo(EntryRealPath(path));
|
fsi = new System.IO.FileInfo(EntryRealPath(path));
|
||||||
|
|
||||||
if (fsi.Exists)
|
if (fsi.Exists)
|
||||||
result = FileSystemInfoToEntry(fsi, path);
|
result = FileSystemInfoToEntry(fsi, path.Parent);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -130,6 +131,36 @@ namespace VirtualFS.Physical
|
|||||||
.Select(f => FileSystemInfoToEntry(f, path));
|
.Select(f => FileSystemInfoToEntry(f, path));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Get directories located under given path.</summary>
|
||||||
|
/// <param name="path">Path to get.</param>
|
||||||
|
/// <param name="mask">Mask to filter out unwanted entries.</param>
|
||||||
|
/// <returns>Returns entry information.</returns>
|
||||||
|
public override IEnumerable<Directory> GetDirectories(Path path, Regex mask = null)
|
||||||
|
{
|
||||||
|
if (!path.IsDirectory)
|
||||||
|
throw new InvalidOperationException(string.Format("Path '{0}' is not a directory, thus it has no entries."));
|
||||||
|
|
||||||
|
return new System.IO.DirectoryInfo(EntryRealPath(path))
|
||||||
|
.EnumerateDirectories()
|
||||||
|
.Where(f => mask == null || mask.IsMatch(f.Name))
|
||||||
|
.Select(f => FileSystemInfoToEntry(f, path) as Directory);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Get files located under given path.</summary>
|
||||||
|
/// <param name="path">Path to get.</param>
|
||||||
|
/// <param name="mask">Mask to filter out unwanted entries.</param>
|
||||||
|
/// <returns>Returns entry information.</returns>
|
||||||
|
public override IEnumerable<File> GetFiles(Path path, Regex mask = null)
|
||||||
|
{
|
||||||
|
if (!path.IsDirectory)
|
||||||
|
throw new InvalidOperationException(string.Format("Path '{0}' is not a directory, thus it has no entries."));
|
||||||
|
|
||||||
|
return new System.IO.DirectoryInfo(EntryRealPath(path))
|
||||||
|
.EnumerateFiles()
|
||||||
|
.Where(f => mask == null || mask.IsMatch(f.Name))
|
||||||
|
.Select(f => FileSystemInfoToEntry(f, path) as File);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Get path to physical file containing entry.</summary>
|
/// <summary>Get path to physical file containing entry.</summary>
|
||||||
/// <param name="path">Virtual file system path.</param>
|
/// <param name="path">Virtual file system path.</param>
|
||||||
/// <returns>Real file system path or <c>null</c> if real
|
/// <returns>Real file system path or <c>null</c> if real
|
||||||
@@ -218,34 +249,51 @@ namespace VirtualFS.Physical
|
|||||||
{
|
{
|
||||||
base.FileOpenRead(path);
|
base.FileOpenRead(path);
|
||||||
|
|
||||||
return new VirtualStream(this, System.IO.File.OpenRead(EntryRealPath(path)));
|
return new VirtualStream(this, System.IO.File.OpenRead(EntryRealPath(path)), path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Opens an existing file for writing.</summary>
|
/// <summary>Opens an existing file for writing.</summary>
|
||||||
/// <param name="path">The file to be opened for writing.</param>
|
/// <param name="path">The file to be opened for writing.</param>
|
||||||
/// <returns>An unshared <see cref="Stream"/> object on
|
/// <param name="append">Append file.</param>
|
||||||
|
/// <returns>An unshared <see cref="Stream" /> object on
|
||||||
/// the specified path with write access.</returns>
|
/// the specified path with write access.</returns>
|
||||||
public override Stream FileOpenWrite(Path path)
|
public override Stream FileOpenWrite(Path path, bool append = false)
|
||||||
{
|
{
|
||||||
base.FileOpenWrite(path);
|
base.FileOpenWrite(path, append);
|
||||||
string realPath = EntryRealPath(path);
|
string realPath = EntryRealPath(path);
|
||||||
|
|
||||||
if (System.IO.File.Exists(realPath))
|
if (System.IO.File.Exists(realPath))
|
||||||
return new VirtualStream(this, new FileStream(realPath, FileMode.Open, FileAccess.Write, FileShare.Read, 4096, FileOptions.None));
|
return new VirtualStream(this, new FileStream(realPath, append ? FileMode.Append : FileMode.Open, FileAccess.Write, FileShare.Read, 4096, FileOptions.None), path);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Creates or overwrites a file in the specified path.</summary>
|
/// <summary>Creates empty file on file system and returns it's description.</summary>
|
||||||
/// <param name="path">The path and name of the file to create.</param>
|
/// <param name="path">The path and name of the file to create.</param>
|
||||||
/// <returns>A <see cref="Stream"/> that provides
|
/// <param name="overwrite">If file exists replace it.</param>
|
||||||
/// write access to the file specified in path.</returns>
|
/// <returns>File description.</returns>
|
||||||
public override Stream FileCreate(Path path)
|
public override File FileTouch(Path path, bool overwrite = false)
|
||||||
{
|
{
|
||||||
base.FileCreate(path);
|
base.FileTouch(path, overwrite);
|
||||||
string realPath = EntryRealPath(path);
|
string realPath = EntryRealPath(path);
|
||||||
|
|
||||||
return new VirtualStream(this, new FileStream(realPath, FileMode.CreateNew, FileAccess.Write, FileShare.Read, 4096));
|
using (var fs = new FileStream(realPath, FileMode.CreateNew, FileAccess.Write, FileShare.Read, 4096))
|
||||||
|
fs.Close();
|
||||||
|
|
||||||
|
return FileSystemInfoToEntry(new System.IO.FileInfo(realPath), path.Parent) as File;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Creates or overwrites a file in the specified path.</summary>
|
||||||
|
/// <param name="path">The path and name of the file to create.</param>
|
||||||
|
/// <param name="overwrite">If file exists replace it.</param>
|
||||||
|
/// <returns>A <see cref="Stream"/> that provides
|
||||||
|
/// write access to the file specified in path.</returns>
|
||||||
|
public override Stream FileCreate(Path path, bool overwrite = false)
|
||||||
|
{
|
||||||
|
base.FileCreate(path, overwrite);
|
||||||
|
string realPath = EntryRealPath(path);
|
||||||
|
|
||||||
|
return new VirtualStream(this, new FileStream(realPath, FileMode.CreateNew, FileAccess.Write, FileShare.Read, 4096), path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Deletes the specified file. An exception is thrown
|
/// <summary>Deletes the specified file. An exception is thrown
|
||||||
@@ -253,7 +301,7 @@ namespace VirtualFS.Physical
|
|||||||
/// <param name="path">The path of the file to be deleted.</param>
|
/// <param name="path">The path of the file to be deleted.</param>
|
||||||
public override void FileDelete(Path path)
|
public override void FileDelete(Path path)
|
||||||
{
|
{
|
||||||
base.FileCreate(path);
|
base.FileDelete(path);
|
||||||
string realPath = EntryRealPath(path);
|
string realPath = EntryRealPath(path);
|
||||||
|
|
||||||
if (System.IO.File.Exists(realPath))
|
if (System.IO.File.Exists(realPath))
|
||||||
|
|||||||
500
VirtualFS/Physical/SFtpFileSystem.cs
Normal file
500
VirtualFS/Physical/SFtpFileSystem.cs
Normal file
@@ -0,0 +1,500 @@
|
|||||||
|
/*
|
||||||
|
* VirtualFS - Virtual File System library.
|
||||||
|
* Copyright (c) 2021, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||||
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using Renci.SshNet;
|
||||||
|
using Renci.SshNet.Sftp;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using VirtualFS.Base;
|
||||||
|
|
||||||
|
namespace VirtualFS.Physical
|
||||||
|
{
|
||||||
|
public class SFtpFileSystem : BaseFileSystem
|
||||||
|
{
|
||||||
|
private string _ftpServer;
|
||||||
|
private string _userName;
|
||||||
|
private string _password;
|
||||||
|
private byte[] _key;
|
||||||
|
private byte[] _fingerPrint;
|
||||||
|
private string _rootPath;
|
||||||
|
private Action<Exception> _exHandler;
|
||||||
|
private SftpClient _client;
|
||||||
|
|
||||||
|
private SftpClient Client
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_client == null)
|
||||||
|
_client = CreateClientPrivate();
|
||||||
|
|
||||||
|
return _client;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Initializes a new instance of the <see cref="SFtpFileSystem"/> class.</summary>
|
||||||
|
/// <param name="host">The SFTP server.</param>
|
||||||
|
/// <param name="userName">SFTP User name.</param>
|
||||||
|
/// <param name="password">SFTP password.</param>
|
||||||
|
/// <param name="key">SFTP private key.</param>
|
||||||
|
/// <param name="fingerPrint">SFTP server fingerprint.</param>
|
||||||
|
/// <param name="rootPath">SFTP root path on server.</param>
|
||||||
|
public SFtpFileSystem(string host, string userName, string password, byte[] key, byte[] fingerPrint, string rootPath, Action<Exception> exHandler = null)
|
||||||
|
: base(false)
|
||||||
|
{
|
||||||
|
_exHandler = exHandler ?? (e => { });
|
||||||
|
_ftpServer = host;
|
||||||
|
_userName = userName;
|
||||||
|
_password = password;
|
||||||
|
_key = key;
|
||||||
|
_fingerPrint = fingerPrint;
|
||||||
|
_rootPath = rootPath;
|
||||||
|
|
||||||
|
if (_ftpServer.EndsWith("/"))
|
||||||
|
_ftpServer = _ftpServer.Substring(0, _ftpServer.Length - 1);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(_rootPath) && _rootPath.EndsWith("/"))
|
||||||
|
_rootPath = _rootPath.Substring(0, _rootPath.Length - 1);
|
||||||
|
|
||||||
|
if (_rootPath == "/")
|
||||||
|
_rootPath = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SftpClient CreateClientPrivate()
|
||||||
|
{
|
||||||
|
var ams = new List<AuthenticationMethod>();
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(_password))
|
||||||
|
ams.Add(new PasswordAuthenticationMethod(_userName, _password));
|
||||||
|
|
||||||
|
if (_key != null && _key.Length > 0)
|
||||||
|
using (var ms = new MemoryStream(_key))
|
||||||
|
ams.Add(new PrivateKeyAuthenticationMethod(_userName, new PrivateKeyFile(ms)));
|
||||||
|
|
||||||
|
var ci = new ConnectionInfo(_ftpServer, _userName, ams.ToArray());
|
||||||
|
|
||||||
|
var client = new SftpClient(ci);
|
||||||
|
|
||||||
|
if (_fingerPrint != null && _fingerPrint.Length > 0)
|
||||||
|
client.HostKeyReceived += (sender, e) =>
|
||||||
|
{
|
||||||
|
e.CanTrust = true;
|
||||||
|
|
||||||
|
if (_fingerPrint.Length == e.FingerPrint.Length)
|
||||||
|
for (var i = 0; i < _fingerPrint.Length; i++)
|
||||||
|
if (_fingerPrint[i] != e.FingerPrint[i])
|
||||||
|
{
|
||||||
|
e.CanTrust = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
e.CanTrust = false;
|
||||||
|
};
|
||||||
|
else
|
||||||
|
client.HostKeyReceived += (sender, e) => e.CanTrust = true;
|
||||||
|
|
||||||
|
client.Connect();
|
||||||
|
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Entry CreateEntry(Path path, ISftpFile ls, bool pathIsParent = true)
|
||||||
|
{
|
||||||
|
Entry e = null;
|
||||||
|
if (ls.IsDirectory)
|
||||||
|
e = new Directory
|
||||||
|
{
|
||||||
|
IsReadOnly = IsReadOnly,
|
||||||
|
Path = pathIsParent ? (path + (ls.Name + "/")) : path,
|
||||||
|
FileSystem = this,
|
||||||
|
Data = ls,
|
||||||
|
};
|
||||||
|
else
|
||||||
|
e = new File
|
||||||
|
{
|
||||||
|
CreationTime = ls.LastWriteTime,
|
||||||
|
LastWriteTime = ls.LastWriteTime,
|
||||||
|
LastAccessTime = ls.LastAccessTime,
|
||||||
|
Size = ls.Length,
|
||||||
|
IsReadOnly = IsReadOnly,
|
||||||
|
Path = pathIsParent ? (path + ls.Name) : path,
|
||||||
|
FileSystem = this,
|
||||||
|
Data = ls,
|
||||||
|
};
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string AdaptPath(SftpClient c, Path path)
|
||||||
|
{
|
||||||
|
var r = path.ToString().Substring(1);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(_rootPath)) {
|
||||||
|
r = (string)(_rootPath + (string)path);
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(r))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
r = r[0] == Path.SeparatorChar ? r.Substring(1) : r;
|
||||||
|
}
|
||||||
|
|
||||||
|
var wd = c.WorkingDirectory;
|
||||||
|
|
||||||
|
if (!wd.EndsWith(Path.SeparatorChar.ToString()))
|
||||||
|
wd += Path.SeparatorChar;
|
||||||
|
|
||||||
|
return wd + r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Get path to physical file containing entry.</summary>
|
||||||
|
/// <param name="path">Virtual file system path.</param>
|
||||||
|
/// <returns>Real file system path or <c>null</c> if real
|
||||||
|
/// path doesn't exist.</returns>
|
||||||
|
/// <remarks>This may not work with all file systems. Implementation
|
||||||
|
/// should return null if real path can't be determined.</remarks>
|
||||||
|
public override string EntryRealPath(Path path)
|
||||||
|
{
|
||||||
|
string uri = _ftpServer;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(_rootPath))
|
||||||
|
uri += _rootPath;
|
||||||
|
|
||||||
|
if (path.Parent != null)
|
||||||
|
uri += (string)path.Parent + (string)path.Name ?? string.Empty;
|
||||||
|
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Check if given path exists.</summary>
|
||||||
|
/// <param name="path">Path to check.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if entry does
|
||||||
|
/// exist, otherwise <c>false</c>.</returns>
|
||||||
|
public override bool Exists(Path path)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Client.Exists(AdaptPath(Client, path));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_exHandler(ex);
|
||||||
|
|
||||||
|
if (_client != null)
|
||||||
|
{
|
||||||
|
_client.Dispose();
|
||||||
|
_client = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Get entries located under given path.</summary>
|
||||||
|
/// <param name="path">Path to get.</param>
|
||||||
|
/// <param name="mask">Mask to filter out unwanted entries.</param>
|
||||||
|
/// <returns>Returns entry information.</returns>
|
||||||
|
public override IEnumerable<Entry> GetEntries(Path path, Regex mask = null)
|
||||||
|
{
|
||||||
|
if (!path.IsDirectory)
|
||||||
|
throw new InvalidOperationException(string.Format("Path '{0}' is not a directory, thus it has no entries.", path));
|
||||||
|
|
||||||
|
List<Entry> result = new List<Entry>();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var lst = Client.ListDirectory(AdaptPath(Client, path));
|
||||||
|
|
||||||
|
foreach (var ls in lst)
|
||||||
|
{
|
||||||
|
if (ls.Name == "." || ls.Name == "..")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Entry e = CreateEntry(path, ls);
|
||||||
|
|
||||||
|
if (e != null)
|
||||||
|
result.Add(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_exHandler(ex);
|
||||||
|
|
||||||
|
if (_client != null)
|
||||||
|
{
|
||||||
|
_client.Dispose();
|
||||||
|
_client = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Get entry located under given path.</summary>
|
||||||
|
/// <param name="path">Path to get.</param>
|
||||||
|
/// <returns>Returns entry information.</returns>
|
||||||
|
public override Entry GetEntry(Path path)
|
||||||
|
{
|
||||||
|
Entry result = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Client.Exists(AdaptPath(Client, path)))
|
||||||
|
result = CreateEntry(path, Client.Get(AdaptPath(Client, path)), false);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Change name of the entry under specified path.</summary>
|
||||||
|
/// <param name="path">The path to entry which name will be changed.</param>
|
||||||
|
/// <param name="newName">The new name of entry.</param>
|
||||||
|
public override void ReName(Path path, string newName)
|
||||||
|
{
|
||||||
|
base.ReName(path, newName);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Client.RenameFile(AdaptPath(Client, path), AdaptPath(Client, path.Parent + newName));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_exHandler(ex);
|
||||||
|
|
||||||
|
if (_client != null)
|
||||||
|
{
|
||||||
|
_client.Dispose();
|
||||||
|
_client = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>Create Directory and return new entry.</summary>
|
||||||
|
/// <param name="path">Path of directory.</param>
|
||||||
|
/// <returns>Created entry.</returns>
|
||||||
|
public override Directory DirectoryCreate(Path path)
|
||||||
|
{
|
||||||
|
base.DirectoryCreate(path);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Client.CreateDirectory(AdaptPath(Client, path));
|
||||||
|
|
||||||
|
if (Client.Exists(AdaptPath(Client, path)))
|
||||||
|
return CreateEntry(path, Client.Get(AdaptPath(Client, path)), false) as Directory;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_exHandler(ex);
|
||||||
|
|
||||||
|
if (_client != null)
|
||||||
|
{
|
||||||
|
_client.Dispose();
|
||||||
|
_client = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Deletes the specified directory and, if
|
||||||
|
/// indicated, any subdirectories in the directory.</summary>
|
||||||
|
/// <param name="path">The path of the directory to remove.</param>
|
||||||
|
/// <param name="recursive">Set <c>true</c> to remove directories,
|
||||||
|
/// subdirectories, and files in path; otherwise <c>false</c>.</param>
|
||||||
|
public override void DirectoryDelete(Path path, bool recursive = false)
|
||||||
|
{
|
||||||
|
base.DirectoryDelete(path, recursive);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Client.Exists(AdaptPath(Client, path)))
|
||||||
|
Client.DeleteDirectory(AdaptPath(Client, path));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_exHandler(ex);
|
||||||
|
|
||||||
|
if (_client != null)
|
||||||
|
{
|
||||||
|
_client.Dispose();
|
||||||
|
_client = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Opens an existing file for reading.</summary>
|
||||||
|
/// <param name="path">The file to be opened for reading.</param>
|
||||||
|
/// <returns>A read-only <see cref="Stream" /> on the specified path.</returns>
|
||||||
|
public override Stream FileOpenRead(Path path)
|
||||||
|
{
|
||||||
|
base.FileOpenRead(path);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Client.OpenRead(AdaptPath(Client, path));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_exHandler(ex);
|
||||||
|
|
||||||
|
if (_client != null)
|
||||||
|
{
|
||||||
|
_client.Dispose();
|
||||||
|
_client = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Opens an existing file for writing.</summary>
|
||||||
|
/// <param name="path">The file to be opened for writing.</param>
|
||||||
|
/// <param name="append">Append file.</param>
|
||||||
|
/// <returns>An unshared <see cref="Stream" /> object on
|
||||||
|
/// the specified path with write access.</returns>
|
||||||
|
public override Stream FileOpenWrite(Path path, bool append = false)
|
||||||
|
{
|
||||||
|
base.FileOpenWrite(path, append);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Client.OpenWrite(AdaptPath(Client, path));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_exHandler(ex);
|
||||||
|
|
||||||
|
if (_client != null)
|
||||||
|
{
|
||||||
|
_client.Dispose();
|
||||||
|
_client = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Creates or overwrites a file in the specified path.</summary>
|
||||||
|
/// <param name="path">The path and name of the file to create.</param>
|
||||||
|
/// <param name="overwrite">If file exists replace it.</param>
|
||||||
|
/// <returns>A <see cref="Stream" /> that provides
|
||||||
|
/// write access to the file specified in path.</returns>
|
||||||
|
public override File FileTouch(Path path, bool overwrite = false)
|
||||||
|
{
|
||||||
|
base.FileTouch(path, overwrite);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var fs = Client.OpenWrite(AdaptPath(Client, path)))
|
||||||
|
fs.Close();
|
||||||
|
|
||||||
|
return new File
|
||||||
|
{
|
||||||
|
CreationTime = DateTime.Now,
|
||||||
|
LastWriteTime = DateTime.Now,
|
||||||
|
LastAccessTime = DateTime.Now,
|
||||||
|
Size = 0,
|
||||||
|
IsReadOnly = false,
|
||||||
|
Path = path,
|
||||||
|
FileSystem = this,
|
||||||
|
Data = null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_exHandler(ex);
|
||||||
|
|
||||||
|
if (_client != null)
|
||||||
|
{
|
||||||
|
_client.Dispose();
|
||||||
|
_client = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Creates or overwrites a file in the specified path.</summary>
|
||||||
|
/// <param name="path">The path and name of the file to create.</param>
|
||||||
|
/// <param name="overwrite">If file exists replace it.</param>
|
||||||
|
/// <returns>A <see cref="Stream" /> that provides
|
||||||
|
/// write access to the file specified in path.</returns>
|
||||||
|
public override Stream FileCreate(Path path, bool overwrite = false)
|
||||||
|
{
|
||||||
|
base.FileCreate(path, overwrite);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Client.OpenWrite(AdaptPath(Client, path));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_exHandler(ex);
|
||||||
|
|
||||||
|
if (_client != null)
|
||||||
|
{
|
||||||
|
_client.Dispose();
|
||||||
|
_client = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>Deletes the specified file. An exception is not thrown
|
||||||
|
/// if the specified file does not exist.</summary>
|
||||||
|
/// <param name="path">The path of the file to be deleted.</param>
|
||||||
|
public override void FileDelete(Path path)
|
||||||
|
{
|
||||||
|
base.FileDelete(path);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Client.Exists(AdaptPath(Client, path)))
|
||||||
|
Client.DeleteFile(AdaptPath(Client, path));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_exHandler(ex);
|
||||||
|
|
||||||
|
if (_client != null)
|
||||||
|
{
|
||||||
|
_client.Dispose();
|
||||||
|
_client = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
211
VirtualFS/Physical/SimpleFtpCachedFileSystem.cs
Normal file
211
VirtualFS/Physical/SimpleFtpCachedFileSystem.cs
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
/*
|
||||||
|
* VirtualFS - Virtual File System library.
|
||||||
|
* Copyright (c) 2013, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||||
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using VirtualFS.Base;
|
||||||
|
using VirtualFS.Implementation;
|
||||||
|
|
||||||
|
namespace VirtualFS.Physical
|
||||||
|
{
|
||||||
|
/// <summary>Ftp file system implementation.</summary>
|
||||||
|
/// <remarks>This is very, very simple implementation
|
||||||
|
/// of a FTP file system. Use this when regular FTP won't do the job.</remarks>
|
||||||
|
public class SimpleFtpCachedFileSystem : SimpleFtpFileSystem
|
||||||
|
{
|
||||||
|
private Dictionary<Path, List<Entry>> _cache = new Dictionary<Path, List<Entry>>();
|
||||||
|
|
||||||
|
/// <summary>Initializes a new instance of the <see cref="FtpFileSystem"/> class.</summary>
|
||||||
|
/// <param name="ftpServer">The FTP server.</param>
|
||||||
|
/// <param name="userName">FTP User name.</param>
|
||||||
|
/// <param name="password">FTP password.</param>
|
||||||
|
/// <param name="rootPath">FTP root path on server.</param>
|
||||||
|
public SimpleFtpCachedFileSystem(string ftpServer, string userName, string password, string rootPath)
|
||||||
|
: base(ftpServer, userName, password, rootPath)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Check if given path exists.</summary>
|
||||||
|
/// <param name="path">Path to check.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if entry does
|
||||||
|
/// exist, otherwise <c>false</c>.</returns>
|
||||||
|
public override bool Exists(Path path)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (path.IsDirectory && path == "/")
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (path.Parent == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var entries = this.GetEntries(path.Parent);
|
||||||
|
|
||||||
|
return entries.Any(x => x.Path == path);
|
||||||
|
}
|
||||||
|
catch (WebException)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Get entries located under given path.</summary>
|
||||||
|
/// <param name="path">Path to get.</param>
|
||||||
|
/// <param name="mask">Mask to filter out unwanted entries.</param>
|
||||||
|
/// <returns>Returns entry information.</returns>
|
||||||
|
public override IEnumerable<Entry> GetEntries(Path path, Regex mask = null)
|
||||||
|
{
|
||||||
|
if (!path.IsDirectory)
|
||||||
|
throw new InvalidOperationException(string.Format("Path '{0}' is not a directory, thus it has no entries.", path));
|
||||||
|
|
||||||
|
List<Entry> cached = null;
|
||||||
|
if (_cache.ContainsKey(path))
|
||||||
|
cached = _cache[path];
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cached = base.GetEntries(path, null).ToList();
|
||||||
|
_cache.Add(path, cached);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask == null)
|
||||||
|
return cached.Select(x => x).ToList();
|
||||||
|
|
||||||
|
return cached.Where(x => mask.IsMatch(x.Path.Name)).Select(x => x).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Change name of the entry under specified path.</summary>
|
||||||
|
/// <param name="path">The path to entry which name will be changed.</param>
|
||||||
|
/// <param name="newName">The new name of entry.</param>
|
||||||
|
public override void ReName(Path path, string newName)
|
||||||
|
{
|
||||||
|
base.ReName(path, newName);
|
||||||
|
|
||||||
|
if (_cache.ContainsKey(path.Parent))
|
||||||
|
{
|
||||||
|
var rf = _cache[path.Parent].FirstOrDefault(x => x.Path == path);
|
||||||
|
|
||||||
|
if (rf != null)
|
||||||
|
rf.Path = (path.Parent + newName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Create Directory and return new entry.</summary>
|
||||||
|
/// <param name="path">Path of directory.</param>
|
||||||
|
/// <returns>Created entry.</returns>
|
||||||
|
public override Directory DirectoryCreate(Path path)
|
||||||
|
{
|
||||||
|
var dir = base.DirectoryCreate(path);
|
||||||
|
|
||||||
|
if (_cache.ContainsKey(path.Parent))
|
||||||
|
{
|
||||||
|
_cache[path.Parent].Add(new Directory
|
||||||
|
{
|
||||||
|
Path = path,
|
||||||
|
FileSystem = this,
|
||||||
|
IsReadOnly = IsReadOnly,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Deletes the specified directory and, if
|
||||||
|
/// indicated, any subdirectories in the directory.</summary>
|
||||||
|
/// <param name="path">The path of the directory to remove.</param>
|
||||||
|
/// <param name="recursive">Set <c>true</c> to remove directories,
|
||||||
|
/// subdirectories, and files in path; otherwise <c>false</c>.</param>
|
||||||
|
public override void DirectoryDelete(Path path, bool recursive = false)
|
||||||
|
{
|
||||||
|
base.DirectoryDelete(path, recursive);
|
||||||
|
|
||||||
|
if (_cache.ContainsKey(path.Parent))
|
||||||
|
{
|
||||||
|
var de = _cache[path.Parent].FirstOrDefault(x => x.Path == path);
|
||||||
|
|
||||||
|
if (de != null)
|
||||||
|
_cache[path.Parent].Remove(de);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Creates empty file on file system and returns it's description.</summary>
|
||||||
|
/// <param name="path">The path and name of the file to create.</param>
|
||||||
|
/// <param name="overwrite">If file exists replace it.</param>
|
||||||
|
/// <returns>File description.</returns>
|
||||||
|
public override File FileTouch(Path path, bool overwrite = false)
|
||||||
|
{
|
||||||
|
var r = base.FileTouch(path, overwrite);
|
||||||
|
|
||||||
|
if (_cache.ContainsKey(path.Parent))
|
||||||
|
_cache[path.Parent].Add(r);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Creates or overwrites a file in the specified path.</summary>
|
||||||
|
/// <param name="path">The path and name of the file to create.</param>
|
||||||
|
/// <param name="overwrite">If file exists replace it.</param>
|
||||||
|
/// <returns>A <see cref="Stream" /> that provides
|
||||||
|
/// write access to the file specified in path.</returns>
|
||||||
|
public override Stream FileCreate(Path path, bool overwrite = false)
|
||||||
|
{
|
||||||
|
var str = base.FileCreate(path, overwrite);
|
||||||
|
|
||||||
|
if (_cache.ContainsKey(path.Parent))
|
||||||
|
{
|
||||||
|
_cache[path.Parent].Add(new File
|
||||||
|
{
|
||||||
|
Path = path,
|
||||||
|
FileSystem = this,
|
||||||
|
IsReadOnly = IsReadOnly,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Deletes the specified file. An exception is not thrown
|
||||||
|
/// if the specified file does not exist.</summary>
|
||||||
|
/// <param name="path">The path of the file to be deleted.</param>
|
||||||
|
public override void FileDelete(Path path)
|
||||||
|
{
|
||||||
|
base.FileDelete(path);
|
||||||
|
|
||||||
|
if (_cache.ContainsKey(path.Parent))
|
||||||
|
{
|
||||||
|
var de = _cache[path.Parent].FirstOrDefault(x => x.Path == path);
|
||||||
|
|
||||||
|
if (de != null)
|
||||||
|
_cache[path.Parent].Remove(de);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
376
VirtualFS/Physical/SimpleFtpFileSystem.cs
Normal file
376
VirtualFS/Physical/SimpleFtpFileSystem.cs
Normal file
@@ -0,0 +1,376 @@
|
|||||||
|
/*
|
||||||
|
* VirtualFS - Virtual File System library.
|
||||||
|
* Copyright (c) 2013, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||||
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using VirtualFS.Base;
|
||||||
|
using VirtualFS.Implementation;
|
||||||
|
|
||||||
|
namespace VirtualFS.Physical
|
||||||
|
{
|
||||||
|
/// <summary>Ftp file system implementation.</summary>
|
||||||
|
/// <remarks>This is very, very simple implementation
|
||||||
|
/// of a FTP file system. Use this when regular FTP won't do the job.</remarks>
|
||||||
|
public class SimpleFtpFileSystem : BaseFileSystem
|
||||||
|
{
|
||||||
|
private string _ftpServer;
|
||||||
|
private string _userName;
|
||||||
|
private string _password;
|
||||||
|
private string _rootPath;
|
||||||
|
|
||||||
|
/// <summary>Initializes a new instance of the <see cref="FtpFileSystem"/> class.</summary>
|
||||||
|
/// <param name="ftpServer">The FTP server.</param>
|
||||||
|
/// <param name="userName">FTP User name.</param>
|
||||||
|
/// <param name="password">FTP password.</param>
|
||||||
|
/// <param name="rootPath">FTP root path on server.</param>
|
||||||
|
public SimpleFtpFileSystem(string ftpServer, string userName, string password, string rootPath)
|
||||||
|
: base(false)
|
||||||
|
{
|
||||||
|
_ftpServer = ftpServer;
|
||||||
|
_userName = userName;
|
||||||
|
_password = password;
|
||||||
|
_rootPath = rootPath;
|
||||||
|
|
||||||
|
if (_ftpServer.EndsWith("/"))
|
||||||
|
_ftpServer = _ftpServer.Substring(0, _ftpServer.Length - 1);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(_rootPath) && _rootPath.EndsWith("/"))
|
||||||
|
_rootPath = _rootPath.Substring(0, _rootPath.Length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Check if given path exists.</summary>
|
||||||
|
/// <param name="path">Path to check.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if entry does
|
||||||
|
/// exist, otherwise <c>false</c>.</returns>
|
||||||
|
public override bool Exists(Path path)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (path.IsDirectory)
|
||||||
|
{
|
||||||
|
FtpDoStuff(WebRequestMethods.Ftp.ListDirectory, path, null);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bool res = false;
|
||||||
|
FtpDoStuff(WebRequestMethods.Ftp.ListDirectory, path, null, r =>
|
||||||
|
{
|
||||||
|
using (StreamReader sr = new StreamReader(r.GetResponseStream()))
|
||||||
|
{
|
||||||
|
string str = sr.ReadLine();
|
||||||
|
while (str != null)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(str) || str == "." || str == "..")
|
||||||
|
{
|
||||||
|
str = sr.ReadLine();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (str == path.Name)
|
||||||
|
{
|
||||||
|
res = true;
|
||||||
|
sr.ReadToEnd();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (WebException)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Get entry located under given path.</summary>
|
||||||
|
/// <param name="path">Path to get.</param>
|
||||||
|
/// <returns>Returns entry information.</returns>
|
||||||
|
public override Entry GetEntry(Path path)
|
||||||
|
{
|
||||||
|
Entry result = null;
|
||||||
|
|
||||||
|
if (Exists(path))
|
||||||
|
{
|
||||||
|
if (path.IsDirectory)
|
||||||
|
result = new Directory();
|
||||||
|
else
|
||||||
|
result = new File();
|
||||||
|
|
||||||
|
result.IsReadOnly = IsReadOnly;
|
||||||
|
result.Path = path;
|
||||||
|
result.FileSystem = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Get path to physical file containing entry.</summary>
|
||||||
|
/// <param name="path">Virtual file system path.</param>
|
||||||
|
/// <returns>Real file system path or <c>null</c> if real
|
||||||
|
/// path doesn't exist.</returns>
|
||||||
|
/// <remarks>This may not work with all file systems. Implementation
|
||||||
|
/// should return null if real path can't be determined.</remarks>
|
||||||
|
public override string EntryRealPath(Path path)
|
||||||
|
{
|
||||||
|
string uri = _ftpServer;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(_rootPath))
|
||||||
|
uri += _rootPath;
|
||||||
|
|
||||||
|
if (path.Parent != null)
|
||||||
|
uri += (string)path.Parent + (string)path.Name ?? string.Empty;
|
||||||
|
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Get entries located under given path.</summary>
|
||||||
|
/// <param name="path">Path to get.</param>
|
||||||
|
/// <param name="mask">Mask to filter out unwanted entries.</param>
|
||||||
|
/// <returns>Returns entry information.</returns>
|
||||||
|
public override IEnumerable<Entry> GetEntries(Path path, Regex mask = null)
|
||||||
|
{
|
||||||
|
if (!path.IsDirectory)
|
||||||
|
throw new InvalidOperationException(string.Format("Path '{0}' is not a directory, thus it has no entries.", path));
|
||||||
|
|
||||||
|
List<Entry> result = new List<Entry>();
|
||||||
|
|
||||||
|
FtpDoStuff(WebRequestMethods.Ftp.ListDirectory, path, null, r =>
|
||||||
|
{
|
||||||
|
using (StreamReader sr = new StreamReader(r.GetResponseStream()))
|
||||||
|
{
|
||||||
|
string str = sr.ReadLine();
|
||||||
|
while (str != null)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(str) || str == "." || str == "..")
|
||||||
|
{
|
||||||
|
str = sr.ReadLine();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask == null || mask.IsMatch(str))
|
||||||
|
{
|
||||||
|
// Here lies a monster. Do not try to understand... just accept.
|
||||||
|
if (str.Contains("."))
|
||||||
|
result.Add(new File()
|
||||||
|
{
|
||||||
|
IsReadOnly = IsReadOnly,
|
||||||
|
Path = path + str,
|
||||||
|
FileSystem = this,
|
||||||
|
});
|
||||||
|
else
|
||||||
|
result.Add(new Directory()
|
||||||
|
{
|
||||||
|
IsReadOnly = IsReadOnly,
|
||||||
|
Path = string.Concat(path, str, "/"),
|
||||||
|
FileSystem = this,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
str = sr.ReadLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Change name of the entry under specified path.</summary>
|
||||||
|
/// <param name="path">The path to entry which name will be changed.</param>
|
||||||
|
/// <param name="newName">The new name of entry.</param>
|
||||||
|
public override void ReName(Path path, string newName)
|
||||||
|
{
|
||||||
|
base.ReName(path, newName);
|
||||||
|
|
||||||
|
System.Net.FtpWebRequest ftp = (System.Net.FtpWebRequest)System.Net.FtpWebRequest.Create(EntryRealPath(path));
|
||||||
|
ftp.Credentials = new System.Net.NetworkCredential(_userName, _password);
|
||||||
|
ftp.Method = WebRequestMethods.Ftp.Rename;
|
||||||
|
ftp.RenameTo = newName;
|
||||||
|
|
||||||
|
using (var r = ftp.GetResponse())
|
||||||
|
r.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Create Directory and return new entry.</summary>
|
||||||
|
/// <param name="path">Path of directory.</param>
|
||||||
|
/// <returns>Created entry.</returns>
|
||||||
|
public override Directory DirectoryCreate(Path path)
|
||||||
|
{
|
||||||
|
base.DirectoryCreate(path);
|
||||||
|
|
||||||
|
if (FtpDoStuff(WebRequestMethods.Ftp.MakeDirectory, path) == FtpStatusCode.PathnameCreated)
|
||||||
|
return (Directory)GetEntry(path);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Deletes the specified directory and, if
|
||||||
|
/// indicated, any subdirectories in the directory.</summary>
|
||||||
|
/// <param name="path">The path of the directory to remove.</param>
|
||||||
|
/// <param name="recursive">Set <c>true</c> to remove directories,
|
||||||
|
/// subdirectories, and files in path; otherwise <c>false</c>.</param>
|
||||||
|
public override void DirectoryDelete(Path path, bool recursive = false)
|
||||||
|
{
|
||||||
|
base.DirectoryDelete(path, recursive);
|
||||||
|
|
||||||
|
FtpDoStuff(WebRequestMethods.Ftp.RemoveDirectory, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Opens an existing file for reading.</summary>
|
||||||
|
/// <param name="path">The file to be opened for reading.</param>
|
||||||
|
/// <returns>A read-only <see cref="Stream" /> on the specified path.</returns>
|
||||||
|
public override Stream FileOpenRead(Path path)
|
||||||
|
{
|
||||||
|
base.FileOpenRead(path);
|
||||||
|
|
||||||
|
System.Net.FtpWebRequest ftp = (System.Net.FtpWebRequest)System.Net.FtpWebRequest.Create(EntryRealPath(path));
|
||||||
|
ftp.Credentials = new System.Net.NetworkCredential(_userName, _password);
|
||||||
|
ftp.Method = System.Net.WebRequestMethods.Ftp.DownloadFile;
|
||||||
|
ftp.KeepAlive = true;
|
||||||
|
|
||||||
|
var resp = ftp.GetResponse();
|
||||||
|
var ret = new VirtualStream(this, resp.GetResponseStream(), path);
|
||||||
|
|
||||||
|
ret.Unspecified = resp;
|
||||||
|
ret.OnClose += (s, e) =>
|
||||||
|
{
|
||||||
|
using (var r = (FtpWebResponse)((VirtualStream)s).Unspecified)
|
||||||
|
r.Close();
|
||||||
|
};
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Opens an existing file for writing.</summary>
|
||||||
|
/// <param name="path">The file to be opened for writing.</param>
|
||||||
|
/// <param name="append">Append file.</param>
|
||||||
|
/// <returns>An unshared <see cref="Stream" /> object on
|
||||||
|
/// the specified path with write access.</returns>
|
||||||
|
public override Stream FileOpenWrite(Path path, bool append = false)
|
||||||
|
{
|
||||||
|
base.FileOpenWrite(path, append);
|
||||||
|
|
||||||
|
return FileOpenWritePrivate(path, append);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Creates empty file on file system and returns it's description.</summary>
|
||||||
|
/// <param name="path">The path and name of the file to create.</param>
|
||||||
|
/// <param name="overwrite">If file exists replace it.</param>
|
||||||
|
/// <returns>File description.</returns>
|
||||||
|
public override File FileTouch(Path path, bool overwrite = false)
|
||||||
|
{
|
||||||
|
base.FileTouch(path, overwrite);
|
||||||
|
using (var fs = FileOpenWritePrivate(path, false))
|
||||||
|
fs.Close();
|
||||||
|
|
||||||
|
return new File()
|
||||||
|
{
|
||||||
|
IsReadOnly = IsReadOnly,
|
||||||
|
Path = path,
|
||||||
|
FileSystem = this,
|
||||||
|
CreationTime = DateTime.Now,
|
||||||
|
LastWriteTime = DateTime.Now,
|
||||||
|
LastAccessTime = DateTime.Now,
|
||||||
|
Size = 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Creates or overwrites a file in the specified path.</summary>
|
||||||
|
/// <param name="path">The path and name of the file to create.</param>
|
||||||
|
/// <param name="overwrite">If file exists replace it.</param>
|
||||||
|
/// <returns>A <see cref="Stream" /> that provides
|
||||||
|
/// write access to the file specified in path.</returns>
|
||||||
|
public override Stream FileCreate(Path path, bool overwrite = false)
|
||||||
|
{
|
||||||
|
base.FileCreate(path, overwrite);
|
||||||
|
|
||||||
|
return FileOpenWritePrivate(path, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Deletes the specified file. An exception is not thrown
|
||||||
|
/// if the specified file does not exist.</summary>
|
||||||
|
/// <param name="path">The path of the file to be deleted.</param>
|
||||||
|
public override void FileDelete(Path path)
|
||||||
|
{
|
||||||
|
base.FileDelete(path);
|
||||||
|
|
||||||
|
FtpDoStuff(WebRequestMethods.Ftp.DeleteFile, path.Parent, path.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Stream FileOpenWritePrivate(Path path, bool append)
|
||||||
|
{
|
||||||
|
System.Net.FtpWebRequest ftp = (System.Net.FtpWebRequest)System.Net.FtpWebRequest.Create(EntryRealPath(path));
|
||||||
|
ftp.Credentials = new System.Net.NetworkCredential(_userName, _password);
|
||||||
|
ftp.Method = append ? WebRequestMethods.Ftp.AppendFile : WebRequestMethods.Ftp.UploadFile;
|
||||||
|
ftp.KeepAlive = true;
|
||||||
|
ftp.UseBinary = true;
|
||||||
|
|
||||||
|
var ret = new VirtualStream(this, ftp.GetRequestStream(), path);
|
||||||
|
|
||||||
|
ret.Unspecified = ftp;
|
||||||
|
ret.OnClose += (s, e) =>
|
||||||
|
{
|
||||||
|
var req = (FtpWebRequest)((VirtualStream)s).Unspecified;
|
||||||
|
using (var resp = req.GetResponse())
|
||||||
|
resp.Close();
|
||||||
|
};
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal FtpStatusCode FtpDoStuff(string method, string path, string filename = null, Action<FtpWebResponse> act = null)
|
||||||
|
{
|
||||||
|
string uri = _ftpServer;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(_rootPath))
|
||||||
|
uri += _rootPath;
|
||||||
|
|
||||||
|
if (path != null)
|
||||||
|
uri += path + filename ?? string.Empty;
|
||||||
|
|
||||||
|
System.Net.FtpWebRequest ftp = (System.Net.FtpWebRequest)System.Net.FtpWebRequest.Create(uri);
|
||||||
|
ftp.Credentials = new System.Net.NetworkCredential(_userName, _password);
|
||||||
|
ftp.Method = method;
|
||||||
|
ftp.KeepAlive = true;
|
||||||
|
|
||||||
|
using (var resp = (FtpWebResponse)ftp.GetResponse())
|
||||||
|
{
|
||||||
|
if (act != null)
|
||||||
|
act(resp);
|
||||||
|
|
||||||
|
return resp.StatusCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,6 +21,8 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||||
<PackageReference Include="SharpZipLib" Version="1.4.2" />
|
<PackageReference Include="SharpZipLib" Version="1.4.2" />
|
||||||
|
<PackageReference Include="SSH.NET" Version="2024.0.0" />
|
||||||
|
<PackageReference Include="System.Net.FtpClient" Version="1.0.5824.34026" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Condition="$(TargetFramework.StartsWith('net4')) AND '$(MSBuildRuntimeType)' == 'Core' AND '$(OS)' != 'Windows_NT'">
|
<ItemGroup Condition="$(TargetFramework.StartsWith('net4')) AND '$(MSBuildRuntimeType)' == 'Core' AND '$(OS)' != 'Windows_NT'">
|
||||||
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="All" />
|
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="All" />
|
||||||
|
|||||||
Reference in New Issue
Block a user