Initial commit
This commit is contained in:
51
.gitignore
vendored
Normal file
51
.gitignore
vendored
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# ---> core
|
||||||
|
*.swp
|
||||||
|
*.*~
|
||||||
|
project.lock.json
|
||||||
|
.DS_Store
|
||||||
|
*.pyc
|
||||||
|
nupkg/
|
||||||
|
|
||||||
|
# Visual Studio Code
|
||||||
|
.vscode
|
||||||
|
|
||||||
|
# Rider
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Dd]ebugPublic/
|
||||||
|
[Rr]elease/
|
||||||
|
[Rr]eleases/
|
||||||
|
x64/
|
||||||
|
x86/
|
||||||
|
build/
|
||||||
|
bld/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
[Oo]ut/
|
||||||
|
msbuild.log
|
||||||
|
msbuild.err
|
||||||
|
msbuild.wrn
|
||||||
|
*/[Bb]in/
|
||||||
|
*/[Oo]bj/
|
||||||
|
*/[Oo]ut/
|
||||||
|
|
||||||
|
# Visual Studio 2015
|
||||||
|
.vs/
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
[Ll]og-*.txt
|
||||||
|
[Ll]ogs/
|
||||||
|
|
||||||
|
# Other
|
||||||
|
appsettings.Development.json
|
||||||
|
configurationCache.bin
|
||||||
|
configurationCache.bin.bak
|
||||||
|
*.cache
|
||||||
47
VirtualFS.Tests/PathTests.cs
Normal file
47
VirtualFS.Tests/PathTests.cs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
namespace VirtualFS.Tests
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
public class PathTests
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public virtual void TestPathCast()
|
||||||
|
{
|
||||||
|
Path p = "/test/path";
|
||||||
|
|
||||||
|
Assert.IsNotNull(p);
|
||||||
|
Assert.AreEqual("/test/path", (string)p);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public virtual void TestPathDirectoryNotDirectory()
|
||||||
|
{
|
||||||
|
Assert.IsFalse(((Path)"/test/path").IsDirectory == ((Path)"/test/path/").IsDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public virtual void TestPathAddPath()
|
||||||
|
{
|
||||||
|
Assert.AreEqual((Path)"/test/path/SomeFile.txt", ((Path)"/test/path/") + "SomeFile.txt");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public virtual void TestPathExt()
|
||||||
|
{
|
||||||
|
Assert.AreEqual("txt", ((Path)"/test/path/SomeFile.txt").GetExtension());
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public virtual void TestPathParent()
|
||||||
|
{
|
||||||
|
Assert.IsNull(new Path().Parent);
|
||||||
|
Assert.AreEqual(new Path(), ((Path)"/test/").Parent);
|
||||||
|
Assert.AreEqual((Path)"/test/", ((Path)"/test/path/").Parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public virtual void TestInvalidPath()
|
||||||
|
{
|
||||||
|
Assert.ThrowsException<InvalidOperationException>(() => new Path("test"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
14
VirtualFS.Tests/Physical/PhysicalFileSystemTests.cs
Normal file
14
VirtualFS.Tests/Physical/PhysicalFileSystemTests.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using VirtualFS.Physical;
|
||||||
|
|
||||||
|
namespace VirtualFS.Tests.Physical
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
public class PhysicalFileSystemTests
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public void TestEnumerate()
|
||||||
|
{
|
||||||
|
Assert.IsTrue(new PhysicalFileSystem(new System.IO.DirectoryInfo("C:\\")).GetEntries("/").Count() > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
44
VirtualFS.Tests/RootFileSystemTest.cs
Normal file
44
VirtualFS.Tests/RootFileSystemTest.cs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
using VirtualFS.Implementation;
|
||||||
|
using VirtualFS.Physical;
|
||||||
|
|
||||||
|
namespace VirtualFS.Tests
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
public class RootFileSystemTest
|
||||||
|
{
|
||||||
|
private RootFileSystem _root;
|
||||||
|
private string _realRootPath = "C:\\Temp\\";
|
||||||
|
|
||||||
|
[TestInitialize]
|
||||||
|
public virtual void SetUp()
|
||||||
|
{
|
||||||
|
if (!System.IO.Directory.Exists(_realRootPath))
|
||||||
|
System.IO.Directory.CreateDirectory(_realRootPath);
|
||||||
|
|
||||||
|
_root = new RootFileSystem();
|
||||||
|
_root.Mount(new PhysicalFileSystem(new System.IO.DirectoryInfo(_realRootPath)), (Path)"/");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public virtual void TestEnumerate()
|
||||||
|
{
|
||||||
|
var root = new RootFileSystem();
|
||||||
|
root.Mount(new PhysicalFileSystem(new System.IO.DirectoryInfo("C:\\")), (Path)"/");
|
||||||
|
root.Mount(new PhysicalFileSystem(new System.IO.DirectoryInfo("C:\\")), (Path)"/");
|
||||||
|
|
||||||
|
Assert.AreEqual(new PhysicalFileSystem(new System.IO.DirectoryInfo("C:\\")).GetEntries("/").Count(), root.GetEntries("/").Count());
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public virtual void TestDirectoryCreateAndDelete()
|
||||||
|
{
|
||||||
|
var dir = _root.Root.Create("Test");
|
||||||
|
|
||||||
|
Assert.IsTrue(System.IO.Directory.Exists(_realRootPath + "Test\\"));
|
||||||
|
|
||||||
|
dir.Delete();
|
||||||
|
|
||||||
|
Assert.IsFalse(System.IO.Directory.Exists(_realRootPath + "Test\\"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
27
VirtualFS.Tests/VirtualFS.Tests.csproj
Normal file
27
VirtualFS.Tests/VirtualFS.Tests.csproj
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
<IsTestProject>true</IsTestProject>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="coverlet.collector" Version="6.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||||
|
<PackageReference Include="MSTest.TestAdapter" Version="3.1.1" />
|
||||||
|
<PackageReference Include="MSTest.TestFramework" Version="3.1.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\VirtualFS\VirtualFS.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Using Include="Microsoft.VisualStudio.TestTools.UnitTesting" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
34
VirtualFS.sln
Normal file
34
VirtualFS.sln
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.0.31903.59
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VirtualFS", "VirtualFS\VirtualFS.csproj", "{1FB8AADC-9568-41A3-AD8E-6181E028A80B}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VirtualFS.Tests", "VirtualFS.Tests\VirtualFS.Tests.csproj", "{D5013B4E-8A1B-4DBB-8FB5-E09935F4F764}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{1FB8AADC-9568-41A3-AD8E-6181E028A80B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{1FB8AADC-9568-41A3-AD8E-6181E028A80B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{1FB8AADC-9568-41A3-AD8E-6181E028A80B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{1FB8AADC-9568-41A3-AD8E-6181E028A80B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{D5013B4E-8A1B-4DBB-8FB5-E09935F4F764}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{D5013B4E-8A1B-4DBB-8FB5-E09935F4F764}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{D5013B4E-8A1B-4DBB-8FB5-E09935F4F764}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{D5013B4E-8A1B-4DBB-8FB5-E09935F4F764}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {22781EB3-2148-4CA4-845A-B55265A7B5C2}
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(MonoDevelopProperties) = preSolution
|
||||||
|
StartupItem = Tester\Tester.csproj
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
391
VirtualFS/Base/BaseFileSystem.cs
Normal file
391
VirtualFS/Base/BaseFileSystem.cs
Normal file
@@ -0,0 +1,391 @@
|
|||||||
|
/*
|
||||||
|
* 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.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace VirtualFS.Base
|
||||||
|
{
|
||||||
|
/// <summary>Basic abstract implementation of file system.</summary>
|
||||||
|
public abstract class BaseFileSystem : IFileSystem
|
||||||
|
{
|
||||||
|
private object _lock = new object();
|
||||||
|
|
||||||
|
private volatile int _openCount = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="BaseFileSystem"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="readOnly">If set to <c>true</c> file system and
|
||||||
|
/// all it's files/directories will be treated as read only.</param>
|
||||||
|
public BaseFileSystem(bool readOnly)
|
||||||
|
{
|
||||||
|
IsReadOnly = readOnly;
|
||||||
|
HighPriority = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Gets reference to root file system.</summary>
|
||||||
|
public virtual IRootFileSystem RootFileSystem { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>Gets or sets a value indicating whether this file system
|
||||||
|
/// has higher priority than others mounted on the same path.</summary>
|
||||||
|
/// <remarks>This value must be set corectly before mount.</remarks>
|
||||||
|
public virtual bool HighPriority { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether this file system is read only.
|
||||||
|
/// </summary>
|
||||||
|
public virtual bool IsReadOnly { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>Gets a value indicating whether this file system is busy.</summary>
|
||||||
|
/// <remarks>Implementations of file systems should set this flag if any
|
||||||
|
/// file is open in this file system or wrap stream into
|
||||||
|
/// <see cref="Implementation.VirtualStream"/>.</remarks>
|
||||||
|
public virtual bool IsBusy
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
bool res = false;
|
||||||
|
|
||||||
|
lock (_lock)
|
||||||
|
res = _openCount > 0;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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 abstract bool Exists(Path path);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get entry located under given path.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">Path to get.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// Returns entry information.
|
||||||
|
/// </returns>
|
||||||
|
public abstract Entry GetEntry(Path path);
|
||||||
|
|
||||||
|
/// <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 abstract string EntryRealPath(Path path);
|
||||||
|
|
||||||
|
/// <summary>Copies an existing file or directory to a new location.</summary>
|
||||||
|
/// <param name="source">Source path of file or directory.</param>
|
||||||
|
/// <param name="destination">Destination path. This path must ba a directory.</param>
|
||||||
|
/// <param name="overwrite">If <c>true</c> the destination files and directories
|
||||||
|
/// can be overwritten; otherwise, <c>false</c>.</param>
|
||||||
|
/// <remarks>This method uses simple yet universal approach.
|
||||||
|
/// it's much slower than native one so implementing file system consider
|
||||||
|
/// better approach for internal copy.</remarks>
|
||||||
|
public virtual void Copy(Path source, Path destination, bool overwrite = false)
|
||||||
|
{
|
||||||
|
InternalCopy(source, destination, overwrite);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Moves an existing file or directory to a new location.</summary>
|
||||||
|
/// <param name="source">Source path of file or directory.</param>
|
||||||
|
/// <param name="destination">Destination path. This path must ba a directory.</param>
|
||||||
|
/// <param name="overwrite">If <c>true</c> the destination files and directories
|
||||||
|
/// can be overwritten; otherwise, <c>false</c>. Files that aren't moved
|
||||||
|
/// will remain on file system.</param>
|
||||||
|
/// <param name="leaveStructure">If <c>true</c> the source directories will be left intact;
|
||||||
|
/// otherwise, <c>false</c>.</param>
|
||||||
|
/// <remarks>This method uses simple yet universal approach.
|
||||||
|
/// it's much slower than native one so implementing file system consider
|
||||||
|
/// better approach for internal move.</remarks>
|
||||||
|
public virtual void Move(Path source, Path destination, bool overwrite = false, bool leaveStructure = false)
|
||||||
|
{
|
||||||
|
InternalCopy(source, destination, overwrite, (s, d) =>
|
||||||
|
{
|
||||||
|
if (!s.IsDirectory)
|
||||||
|
FileDelete(s);
|
||||||
|
else if (s.IsDirectory && !leaveStructure)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
DirectoryDelete(s, false);
|
||||||
|
}
|
||||||
|
catch (IOException)
|
||||||
|
{
|
||||||
|
// We swallow IOException, cause it's suggests that directory contains files
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InternalCopy(Path source, Path destination, bool overwrite, Action<Path, Path> copySuccess = null)
|
||||||
|
{
|
||||||
|
if (source == destination)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!destination.IsDirectory)
|
||||||
|
throw new InvalidOperationException(string.Format("Can't copy to non directory path ('{0}').", destination));
|
||||||
|
|
||||||
|
if (!source.IsDirectory)
|
||||||
|
InternalCopySingleFile(source, destination, overwrite, copySuccess);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Queue<Tuple<Directory, Directory>> cpy = new Queue<Tuple<Directory, Directory>>();
|
||||||
|
Stack<Tuple<Directory, Directory>> del = new Stack<Tuple<Directory, Directory>>();
|
||||||
|
|
||||||
|
if (!Exists(destination))
|
||||||
|
DirectoryCreate(destination);
|
||||||
|
|
||||||
|
cpy.Enqueue(new Tuple<Directory, Directory>(GetEntry(source) as Directory, GetEntry(destination) as Directory));
|
||||||
|
|
||||||
|
while (cpy.Count > 0)
|
||||||
|
{
|
||||||
|
var dir = cpy.Dequeue();
|
||||||
|
|
||||||
|
if (dir != null)
|
||||||
|
{
|
||||||
|
if (copySuccess != null)
|
||||||
|
del.Push(dir);
|
||||||
|
|
||||||
|
foreach (var e in dir.Item1.GetEntries())
|
||||||
|
{
|
||||||
|
if (e is Directory)
|
||||||
|
{
|
||||||
|
Directory np = null;
|
||||||
|
Path newDir = dir.Item2.Path + e.Path.Name;
|
||||||
|
|
||||||
|
if (!Exists(newDir))
|
||||||
|
np = DirectoryCreate(newDir);
|
||||||
|
|
||||||
|
cpy.Enqueue(new Tuple<Directory, Directory>(e as Directory, np ?? GetEntry(newDir) as Directory));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
InternalCopySingleFile(e.Path, dir.Item2.Path, overwrite, copySuccess);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (copySuccess != null)
|
||||||
|
{
|
||||||
|
Tuple<Directory, Directory> item = null;
|
||||||
|
while ((item = del.Pop()) != null)
|
||||||
|
copySuccess(item.Item1.Path, item.Item2.Path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InternalCopySingleFile(Path source, Path destination, bool overwrite, Action<Path, Path> copySuccess)
|
||||||
|
{
|
||||||
|
var dst = destination.Append(source.Name);
|
||||||
|
|
||||||
|
if (!Exists(source))
|
||||||
|
throw new InvalidOperationException("Source file doesn't exist.");
|
||||||
|
|
||||||
|
if (!overwrite && Exists(dst))
|
||||||
|
throw new InvalidOperationException("Destination file already exist.");
|
||||||
|
|
||||||
|
Directory dir = (Directory)GetEntry(destination);
|
||||||
|
|
||||||
|
if (dir.IsReadOnly || dir.FileSystem.IsReadOnly)
|
||||||
|
throw new InvalidOperationException("Can't copy on read only file system.");
|
||||||
|
|
||||||
|
dir
|
||||||
|
.FileCreate(source.Name)
|
||||||
|
.WriteAllBytes(((File)GetEntry(source)).ReadAllBytes());
|
||||||
|
|
||||||
|
if (copySuccess != null)
|
||||||
|
copySuccess(source, destination.Append(source.Name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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 virtual void ReName(Path path, string newName)
|
||||||
|
{
|
||||||
|
if (path.Name == newName)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (IsReadOnly)
|
||||||
|
throw new InvalidOperationException(string.Format("Can't rename file from '{0}' to '{1}' on read only file system.", path, newName));
|
||||||
|
|
||||||
|
if (!Exists(path))
|
||||||
|
throw new InvalidOperationException(string.Format("Can't rename non existing entry '{0}' to '{1}'.", path, newName));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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 abstract IEnumerable<Entry> GetEntries(Path path, Regex mask = null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create Directory and return new entry.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">Path of directory.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// Created entry.
|
||||||
|
/// </returns>
|
||||||
|
public virtual Directory DirectoryCreate(Path path)
|
||||||
|
{
|
||||||
|
if (IsReadOnly)
|
||||||
|
throw new InvalidOperationException(string.Format("Can't create directory ('{0}') on read only file system.", path));
|
||||||
|
|
||||||
|
if (!path.IsDirectory)
|
||||||
|
throw new InvalidOperationException(string.Format("Can't create directory from file path ('{0}').", path));
|
||||||
|
|
||||||
|
if (path.Parent == null)
|
||||||
|
throw new InvalidOperationException("Can't create root directory of a file system.");
|
||||||
|
|
||||||
|
if (!Exists(path.Parent))
|
||||||
|
throw new InvalidOperationException("Can't create entire directory path.");
|
||||||
|
|
||||||
|
if (Exists(path))
|
||||||
|
throw new InvalidOperationException("Can't create directory that already exist.");
|
||||||
|
|
||||||
|
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 virtual void DirectoryDelete(Path path, bool recursive = false)
|
||||||
|
{
|
||||||
|
if (IsReadOnly)
|
||||||
|
throw new InvalidOperationException(string.Format("Can't delete directory ('{0}') on read only file system.", path));
|
||||||
|
|
||||||
|
if (path == Path.Root)
|
||||||
|
throw new InvalidOperationException(string.Format("Can't delete root directory.", path));
|
||||||
|
|
||||||
|
if (!path.IsDirectory)
|
||||||
|
throw new InvalidOperationException(string.Format("Can't delete directory from file path ('{0}').", path));
|
||||||
|
|
||||||
|
if (!Exists(path))
|
||||||
|
throw new InvalidOperationException("Can't delete directory that doesn't exist.");
|
||||||
|
|
||||||
|
if (!recursive && GetEntries(path).Any())
|
||||||
|
throw new InvalidOperationException("Can't delete directory that contains entries.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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("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>
|
||||||
|
/// <returns>A <see cref="Stream"/> that provides
|
||||||
|
/// write access to the file specified in path.</returns>
|
||||||
|
public virtual Stream FileCreate(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 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.");
|
||||||
|
|
||||||
|
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 virtual void FileDelete(Path path)
|
||||||
|
{
|
||||||
|
if (path.IsDirectory)
|
||||||
|
throw new InvalidOperationException(string.Format("Can't delete directory path ('{0}') as file.", path));
|
||||||
|
|
||||||
|
if (IsReadOnly)
|
||||||
|
throw new InvalidOperationException(string.Format("Can't delete file ('{0}') from read only file system.", path));
|
||||||
|
|
||||||
|
if (!Exists(path))
|
||||||
|
throw new InvalidOperationException("Can't delete not existing file.");
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void IncreaseOpened()
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
_openCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void DecreaseOpened()
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
_openCount--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Performs application-defined tasks associated with
|
||||||
|
/// freeing, releasing, or resetting unmanaged resources.</summary>
|
||||||
|
public virtual void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
228
VirtualFS/Compressed/ZipReadFileSystem.cs
Normal file
228
VirtualFS/Compressed/ZipReadFileSystem.cs
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
/*
|
||||||
|
* 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.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using ICSharpCode.SharpZipLib.Zip;
|
||||||
|
using VirtualFS.Base;
|
||||||
|
using VirtualFS.Implementation;
|
||||||
|
|
||||||
|
namespace VirtualFS.Compressed
|
||||||
|
{
|
||||||
|
/// <summary>Zip file based read only file system implementation.</summary>
|
||||||
|
public class ZipReadFileSystem : BaseFileSystem
|
||||||
|
{
|
||||||
|
/// <summary>Gets the Zip file.</summary>
|
||||||
|
public ZipFile File { get; private set; }
|
||||||
|
|
||||||
|
private Dictionary<Path, Entry> _pathToEntry;
|
||||||
|
|
||||||
|
/// <summary>Gets the assembly on which file system operates.</summary>
|
||||||
|
public Assembly Assembly { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>Initializes a new instance of the <see cref="ZipReadFileSystem"/> class.</summary>
|
||||||
|
/// <param name="stream">The stream with zip file.</param>
|
||||||
|
/// <param name="root">The root path.</param>
|
||||||
|
/// <param name="filter">The file filter.</param>
|
||||||
|
public ZipReadFileSystem(System.IO.Stream stream, Path root = null, Regex filter = null)
|
||||||
|
: base(true)
|
||||||
|
{
|
||||||
|
File = new ZipFile(stream);
|
||||||
|
|
||||||
|
foreach (ZipEntry zipEntry in File)
|
||||||
|
{
|
||||||
|
if (filter == null || filter.IsMatch(zipEntry.Name))
|
||||||
|
{
|
||||||
|
if (zipEntry.IsDirectory)
|
||||||
|
{
|
||||||
|
var path = ZipEntryToPath(zipEntry, root, true);
|
||||||
|
|
||||||
|
if (path != null)
|
||||||
|
_pathToEntry.Add(path, new Directory
|
||||||
|
{
|
||||||
|
Data = zipEntry,
|
||||||
|
Path = path,
|
||||||
|
FileSystem = this,
|
||||||
|
IsReadOnly = true,
|
||||||
|
CreationTime = zipEntry.DateTime,
|
||||||
|
LastAccessTime = zipEntry.DateTime,
|
||||||
|
LastWriteTime = zipEntry.DateTime,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (zipEntry.IsFile)
|
||||||
|
{
|
||||||
|
var path = ZipEntryToPath(zipEntry, root);
|
||||||
|
|
||||||
|
if (path != null)
|
||||||
|
_pathToEntry.Add(path, new File
|
||||||
|
{
|
||||||
|
Data = zipEntry,
|
||||||
|
Path = path,
|
||||||
|
FileSystem = this,
|
||||||
|
IsReadOnly = true,
|
||||||
|
CreationTime = zipEntry.DateTime,
|
||||||
|
LastAccessTime = zipEntry.DateTime,
|
||||||
|
LastWriteTime = zipEntry.DateTime,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ugly but it works (fix this - performance)
|
||||||
|
_pathToEntry
|
||||||
|
.Keys
|
||||||
|
.Where(p => !p.IsDirectory)
|
||||||
|
.ToList()
|
||||||
|
.ForEach(p =>
|
||||||
|
{
|
||||||
|
var x = p.Parent;
|
||||||
|
while (x != null)
|
||||||
|
{
|
||||||
|
Exists(x);
|
||||||
|
x = x.Parent;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Path ZipEntryToPath(ZipEntry zipEntry, Path root, bool dir = false)
|
||||||
|
{
|
||||||
|
var ret = zipEntry.Name.Replace('\\', Path.SeparatorChar);
|
||||||
|
if (ret[0] != Path.SeparatorChar)
|
||||||
|
ret = Path.SeparatorChar + ret;
|
||||||
|
|
||||||
|
if (dir && !ret.EndsWith(Path.SeparatorChar.ToString()))
|
||||||
|
ret += Path.SeparatorChar;
|
||||||
|
|
||||||
|
if (root != null)
|
||||||
|
{
|
||||||
|
if (root.IsParentOf(ret))
|
||||||
|
ret = ((Path)ret).RemoveParent(root);
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
bool ret = path == Path.Root ||
|
||||||
|
(_pathToEntry.ContainsKey(path) &&
|
||||||
|
((_pathToEntry[path] is Directory) == path.IsDirectory));
|
||||||
|
|
||||||
|
if (!ret && _pathToEntry.Keys.Any(x => x.IsParentOf(path)))
|
||||||
|
{
|
||||||
|
_pathToEntry.Add(path, new Directory()
|
||||||
|
{
|
||||||
|
Data = null,
|
||||||
|
IsReadOnly = IsReadOnly,
|
||||||
|
Path = path,
|
||||||
|
FileSystem = this,
|
||||||
|
CreationTime = DateTime.Now,
|
||||||
|
LastAccessTime = DateTime.Now,
|
||||||
|
LastWriteTime = DateTime.Now,
|
||||||
|
});
|
||||||
|
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
if (!Exists(path))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return _pathToEntry[path];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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."));
|
||||||
|
|
||||||
|
if (Exists(path))
|
||||||
|
return _pathToEntry
|
||||||
|
.Where(k => k.Key.Parent == path)
|
||||||
|
.Where(k => mask == null || mask.IsMatch(k.Key.Name))
|
||||||
|
.Select(v => v.Value);
|
||||||
|
|
||||||
|
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 override System.IO.Stream FileOpenRead(Path path)
|
||||||
|
{
|
||||||
|
base.FileOpenRead(path);
|
||||||
|
|
||||||
|
return new VirtualStream(this, File.GetInputStream((ZipEntry)_pathToEntry[path].Data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Performs application-defined tasks associated with
|
||||||
|
/// freeing, releasing, or resetting unmanaged resources.</summary>
|
||||||
|
public override void Dispose()
|
||||||
|
{
|
||||||
|
if (File != null)
|
||||||
|
{
|
||||||
|
// Makes close also shut the underlying stream
|
||||||
|
File.IsStreamOwner = true;
|
||||||
|
|
||||||
|
// Ensure we release resources
|
||||||
|
File.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
123
VirtualFS/Directory.cs
Normal file
123
VirtualFS/Directory.cs
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
* 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.Text.RegularExpressions;
|
||||||
|
using VirtualFS.Extensions;
|
||||||
|
|
||||||
|
namespace VirtualFS
|
||||||
|
{
|
||||||
|
/// <summary>Class representing directory entry.</summary>
|
||||||
|
public class Directory : Entry
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="Directory"/> class.
|
||||||
|
/// </summary>
|
||||||
|
internal Directory()
|
||||||
|
{
|
||||||
|
IsDirectory = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Enumerate entries 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 IEnumerable<Entry> GetEntries(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.GetEntries(FullPath, mask) :
|
||||||
|
FileSystem.GetEntries(Path, mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Create Directory and return new entry.</summary>
|
||||||
|
/// <param name="name">Name of directory.</param>
|
||||||
|
/// <param name="forceLocal">Force usage of local file
|
||||||
|
/// system, to which directory is bound.</param>
|
||||||
|
/// <returns>Created entry.</returns>
|
||||||
|
public Directory Create(string name, bool forceLocal = false)
|
||||||
|
{
|
||||||
|
return !forceLocal && FileSystem.RootFileSystem != null ?
|
||||||
|
FileSystem.RootFileSystem.DirectoryCreate(FullPath.Append(string.Format("{0}{1}",
|
||||||
|
name.Validated("name", invalidChars: Path.InvalidFileChars, invalidSeq: Path.InvalidPathSequences),
|
||||||
|
Path.SeparatorChar))) :
|
||||||
|
FileSystem.DirectoryCreate(Path.Append(string.Format("{0}{1}",
|
||||||
|
name.Validated("name", invalidChars: Path.InvalidFileChars, invalidSeq: Path.InvalidPathSequences),
|
||||||
|
Path.SeparatorChar)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Deletes the specified directory and, if
|
||||||
|
/// indicated, any subdirectories in the directory.</summary>
|
||||||
|
/// <param name="recursive">Set <c>true</c> to remove directories,
|
||||||
|
/// subdirectories, and files in path; otherwise <c>false</c>.</param>
|
||||||
|
/// <param name="forceLocal">Force usage of local file
|
||||||
|
/// system, to which directory is bound.</param>
|
||||||
|
public void Delete(bool recursive = false, bool forceLocal = false)
|
||||||
|
{
|
||||||
|
if (!forceLocal && FileSystem.RootFileSystem != null)
|
||||||
|
FileSystem.RootFileSystem.DirectoryDelete(FullPath, recursive);
|
||||||
|
else
|
||||||
|
FileSystem.DirectoryDelete(Path, recursive);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Creates or overwrites a file in the specified path.</summary>
|
||||||
|
/// <param name="name">The of the file to create.</param>
|
||||||
|
/// <param name="forceLocal">Force usage of local file
|
||||||
|
/// system, to which directory is bound.</param>
|
||||||
|
/// <remarks>Created file is empty.</remarks>
|
||||||
|
/// <returns>The <see cref="File"/> entry information.</returns>
|
||||||
|
public File FileCreate(string name, bool forceLocal = false)
|
||||||
|
{
|
||||||
|
if (!forceLocal && FileSystem.RootFileSystem != null)
|
||||||
|
{
|
||||||
|
var path = FullPath.Append(
|
||||||
|
name.Validated("name", invalidChars: Path.InvalidFileChars, invalidSeq: Path.InvalidPathSequences));
|
||||||
|
|
||||||
|
using (var stream = FileSystem.RootFileSystem.FileCreate(path))
|
||||||
|
stream.Close();
|
||||||
|
|
||||||
|
return FileSystem.RootFileSystem.GetEntry(path) as File;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var path = Path.Append(
|
||||||
|
name.Validated("name", invalidChars: Path.InvalidFileChars, invalidSeq: Path.InvalidPathSequences));
|
||||||
|
|
||||||
|
using (var stream = FileSystem.FileCreate(path))
|
||||||
|
stream.Close();
|
||||||
|
|
||||||
|
return FileSystem.GetEntry(path) as File;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
215
VirtualFS/EmbeddedResource/EmbeddedResourceFileSystem.cs
Normal file
215
VirtualFS/EmbeddedResource/EmbeddedResourceFileSystem.cs
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
/*
|
||||||
|
* 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.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using VirtualFS.Base;
|
||||||
|
using VirtualFS.Implementation;
|
||||||
|
|
||||||
|
namespace VirtualFS.EmbeddedResource
|
||||||
|
{
|
||||||
|
/// <summary>Embedded resource file system implementation.</summary>
|
||||||
|
public class EmbeddedResourceFileSystem : BaseFileSystem
|
||||||
|
{
|
||||||
|
private static object _lock = new object();
|
||||||
|
|
||||||
|
private Dictionary<Path, Entry> _pathToResource;
|
||||||
|
|
||||||
|
/// <summary>Gets the assembly on which file system operates.</summary>
|
||||||
|
public Assembly Assembly { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>Initializes a new instance of the <see cref="EmbeddedResourceFileSystem"/> class.</summary>
|
||||||
|
/// <param name="assembly">The assembly.</param>
|
||||||
|
/// <param name="rootNamespace">The root namespace.</param>
|
||||||
|
/// <param name="filter">The filter.</param>
|
||||||
|
public EmbeddedResourceFileSystem(Assembly assembly, string rootNamespace = null, Regex filter = null)
|
||||||
|
: base(true)
|
||||||
|
{
|
||||||
|
Assembly = assembly;
|
||||||
|
|
||||||
|
var fi = new System.IO.FileInfo(Assembly.Location);
|
||||||
|
|
||||||
|
_pathToResource = Assembly.GetManifestResourceNames()
|
||||||
|
.Where(x => (string.IsNullOrEmpty(rootNamespace) || x.StartsWith(rootNamespace)) && (filter == null || filter.IsMatch(x)))
|
||||||
|
.Select(x => new File()
|
||||||
|
{
|
||||||
|
Data = x,
|
||||||
|
IsReadOnly = IsReadOnly,
|
||||||
|
Path = ResourceToPath(rootNamespace, x),
|
||||||
|
FileSystem = this,
|
||||||
|
CreationTime = fi.CreationTimeUtc,
|
||||||
|
LastAccessTime = fi.LastAccessTime,
|
||||||
|
LastWriteTime = fi.LastWriteTime,
|
||||||
|
})
|
||||||
|
.ToDictionary(
|
||||||
|
k => k.Path,
|
||||||
|
v => (Entry)v);
|
||||||
|
|
||||||
|
_pathToResource
|
||||||
|
.Select(p => p.Key)
|
||||||
|
.Where(p => ((string)p).LastIndexOf(Path.SeparatorChar) > 0)
|
||||||
|
.Select(p => (Path)((string)p).Substring(0, ((string)p).LastIndexOf(Path.SeparatorChar) + 1))
|
||||||
|
.Distinct()
|
||||||
|
.ToList()
|
||||||
|
.ForEach(p => _pathToResource.Add(p, new Directory()
|
||||||
|
{
|
||||||
|
Data = null,
|
||||||
|
IsReadOnly = IsReadOnly,
|
||||||
|
Path = p,
|
||||||
|
FileSystem = this,
|
||||||
|
CreationTime = fi.CreationTimeUtc,
|
||||||
|
LastAccessTime = fi.LastAccessTime,
|
||||||
|
LastWriteTime = fi.LastWriteTime,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Ugly but it works (fix this - performance)
|
||||||
|
_pathToResource
|
||||||
|
.Keys
|
||||||
|
.Where(p => !p.IsDirectory)
|
||||||
|
.ToList()
|
||||||
|
.ForEach(p =>
|
||||||
|
{
|
||||||
|
var x = p.Parent;
|
||||||
|
while (x != null)
|
||||||
|
{
|
||||||
|
Exists(x);
|
||||||
|
x = x.Parent;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Path ResourceToPath(string rootNamespace, string resName)
|
||||||
|
{
|
||||||
|
string ret = resName;
|
||||||
|
|
||||||
|
int extDot = 0;
|
||||||
|
if (!string.IsNullOrEmpty(rootNamespace))
|
||||||
|
ret = ret.Substring(rootNamespace.Length).TrimStart('.');
|
||||||
|
|
||||||
|
extDot = ret.LastIndexOf('.');
|
||||||
|
ret = ret.Replace('.', Path.SeparatorChar);
|
||||||
|
if (extDot >= 0)
|
||||||
|
{
|
||||||
|
ret = ret
|
||||||
|
.Remove(extDot, 1)
|
||||||
|
.Insert(extDot, ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (Path)ret.Insert(0, Path.SeparatorChar.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
ret = path == Path.Root ||
|
||||||
|
(_pathToResource.ContainsKey(path) &&
|
||||||
|
((_pathToResource[path] is Directory) == path.IsDirectory));
|
||||||
|
|
||||||
|
if (!ret && path.IsDirectory && _pathToResource.Keys.Any(x => x.IsParentOf(path)))
|
||||||
|
{
|
||||||
|
var fi = new System.IO.FileInfo(Assembly.Location);
|
||||||
|
|
||||||
|
_pathToResource.Add(path, new Directory()
|
||||||
|
{
|
||||||
|
Data = null,
|
||||||
|
IsReadOnly = IsReadOnly,
|
||||||
|
Path = path,
|
||||||
|
FileSystem = this,
|
||||||
|
CreationTime = fi.CreationTimeUtc,
|
||||||
|
LastAccessTime = fi.LastAccessTime,
|
||||||
|
LastWriteTime = fi.LastWriteTime,
|
||||||
|
});
|
||||||
|
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
if (!Exists(path))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return _pathToResource[path];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
return this.Assembly.Location;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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."));
|
||||||
|
|
||||||
|
if (Exists(path))
|
||||||
|
return _pathToResource
|
||||||
|
.Where(k => k.Key.Parent == path)
|
||||||
|
.Where(k => mask == null || mask.IsMatch(k.Key.Name))
|
||||||
|
.Select(v => v.Value);
|
||||||
|
|
||||||
|
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 override System.IO.Stream FileOpenRead(Path path)
|
||||||
|
{
|
||||||
|
base.FileOpenRead(path);
|
||||||
|
|
||||||
|
return new VirtualStream(this, Assembly.GetManifestResourceStream(_pathToResource[path].Data.ToString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
99
VirtualFS/Entry.cs
Normal file
99
VirtualFS/Entry.cs
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
namespace VirtualFS
|
||||||
|
{
|
||||||
|
/// <summary>Basic file system entry.</summary>
|
||||||
|
public class Entry
|
||||||
|
{
|
||||||
|
/// <summary>Gets file system on which entry is located.</summary>
|
||||||
|
public virtual IFileSystem FileSystem { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>Gets a value indicating whether this entry is read only.</summary>
|
||||||
|
public virtual bool IsReadOnly { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>Gets a value indicating whether this is a directory entry.</summary>
|
||||||
|
public virtual bool IsDirectory { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>Gets path to entry on local file system.</summary>
|
||||||
|
public virtual Path Path { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>Gets full path to entry including mount point.</summary>
|
||||||
|
public Path FullPath { get { return (FileSystem == null || FileSystem.RootFileSystem == null) ? null : Path.AddParent(FileSystem.RootFileSystem.GetMountPath(FileSystem)); } }
|
||||||
|
|
||||||
|
/// <summary>Gets or sets the creation time, in coordinated universal time (UTC), of the current file or directory.</summary>
|
||||||
|
public virtual DateTime CreationTime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Gets or sets the time, in coordinated universal time (UTC), that the current file or directory was last accessed.</summary>
|
||||||
|
public virtual DateTime LastAccessTime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Gets or sets the time, in coordinated universal time (UTC), when the current file or directory was last written to.</summary>
|
||||||
|
public DateTime LastWriteTime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Gets or sets additional internal data for file system entry.</summary>
|
||||||
|
internal object Data { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified <see cref="System.Object" /> is equal to this instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The <see cref="System.Object" /> to compare with this instance.</param>
|
||||||
|
/// <returns> Returns <c>true</c> if the specified <see cref="System.Object" />
|
||||||
|
/// is equal to this instance; otherwise, <c>false</c>.</returns>
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
if (obj is Entry)
|
||||||
|
return Equals((Entry)obj);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified <see cref="Entry" /> is equal to this instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="other">The <see cref="Entry" /> to compare with this instance.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if the specified <see cref="Entry" /> is equal
|
||||||
|
/// to this instance; otherwise, <c>false</c>.</returns>
|
||||||
|
public bool Equals(Entry other)
|
||||||
|
{
|
||||||
|
return other.Path.Equals(Path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a hash code for this instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
|
||||||
|
/// </returns>
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return Path.GetHashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
128
VirtualFS/Extensions/Validators.cs
Normal file
128
VirtualFS/Extensions/Validators.cs
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
namespace VirtualFS.Extensions
|
||||||
|
{
|
||||||
|
internal static class Validators
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a new validated string using the rules given.
|
||||||
|
/// </summary>
|
||||||
|
/// <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="canbeNull">True if the returned string can be null.</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="trimStart">True to left-trim the returned string.</param>
|
||||||
|
/// <param name="trimEnd">True to right-trim the returned string.</param>
|
||||||
|
/// <param name="minLen">If >= 0, the min valid length for the returned string.</param>
|
||||||
|
/// <param name="maxLen">If >= 0, the max valid length for the returned string.</param>
|
||||||
|
/// <param name="padLeft">If not '\0', the character to use to left-pad the returned string if needed.</param>
|
||||||
|
/// <param name="padRight">If not '\0', the character to use to right-pad the returned string if needed.</param>
|
||||||
|
/// <param name="invalidChars">If not null, an array containing invalid chars that must not appear in the returned
|
||||||
|
/// string.</param>
|
||||||
|
/// <param name="validChars">If not null, an array containing the only characters that are considered valid for the
|
||||||
|
/// returned string.</param>
|
||||||
|
/// <param name="invalidSeq">If not null, an array containing the sequences that are not allowed.</param>
|
||||||
|
/// <returns>A new validated string.</returns>
|
||||||
|
public static string Validated(this string source, string desc = null,
|
||||||
|
bool canbeNull = false, bool canbeEmpty = false,
|
||||||
|
bool trim = true, bool trimStart = false, bool trimEnd = false,
|
||||||
|
int minLen = -1, int maxLen = -1, char padLeft = '\0', char padRight = '\0',
|
||||||
|
char[] invalidChars = null, char[] validChars = null, string[] invalidSeq = null)
|
||||||
|
{
|
||||||
|
// Assuring a valid descriptor...
|
||||||
|
if (string.IsNullOrWhiteSpace(desc)) desc = "Source";
|
||||||
|
|
||||||
|
// Validating if null sources are accepted...
|
||||||
|
if (source == null)
|
||||||
|
{
|
||||||
|
if (!canbeNull) throw new ArgumentNullException(desc, string.Format("{0} cannot be null.", desc));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trimming if needed...
|
||||||
|
if (trim && !(trimStart || trimEnd)) source = source.Trim();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (trimStart) source = source.TrimStart(' ');
|
||||||
|
if (trimEnd) source = source.TrimEnd(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjusting lenght...
|
||||||
|
if (minLen > 0)
|
||||||
|
{
|
||||||
|
if (padLeft != '\0') source = source.PadLeft(minLen, padLeft);
|
||||||
|
if (padRight != '\0') source = source.PadRight(minLen, padRight);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxLen > 0)
|
||||||
|
{
|
||||||
|
if (padLeft != '\0') source = source.PadLeft(maxLen, padLeft);
|
||||||
|
if (padRight != '\0') source = source.PadRight(maxLen, padRight);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validating emptyness and lenghts...
|
||||||
|
if (source.Length == 0)
|
||||||
|
{
|
||||||
|
if (!canbeEmpty) throw new ArgumentException(string.Format("{0} cannot be empty.", desc));
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (minLen >= 0 && source.Length < minLen) throw new ArgumentException(string.Format("Lenght of {0} '{1}' is lower than '{2}'.", desc, source, minLen));
|
||||||
|
if (maxLen >= 0 && source.Length > maxLen) throw new ArgumentException(string.Format("Lenght of {0} '{1}' is bigger than '{2}'.", desc, source, maxLen));
|
||||||
|
|
||||||
|
// Checking invalid chars...
|
||||||
|
if (invalidChars != null)
|
||||||
|
{
|
||||||
|
int n = source.IndexOfAny(invalidChars);
|
||||||
|
if (n >= 0) throw new ArgumentException(string.Format("Invalid character '{0}' found in {1} '{2}'.", source[n], desc, source));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checking valid chars...
|
||||||
|
if (validChars != null)
|
||||||
|
{
|
||||||
|
int n = validChars.ToString().IndexOfAny(source.ToCharArray());
|
||||||
|
if (n >= 0)
|
||||||
|
throw new ArgumentException(string.Format("Invalid character '{0}' found in {1} '{2}'.", validChars.ToString()[n], desc, source));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checking invalid sequences
|
||||||
|
if (invalidSeq != null)
|
||||||
|
{
|
||||||
|
foreach (var seq in invalidSeq)
|
||||||
|
if (source.IndexOf(seq) >= 0)
|
||||||
|
throw new ArgumentException(string.Format("Invalid sequence '{0}' found in {1} '{2}'.", seq, desc, source));
|
||||||
|
}
|
||||||
|
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
207
VirtualFS/File.cs
Normal file
207
VirtualFS/File.cs
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
/*
|
||||||
|
* 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.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace VirtualFS
|
||||||
|
{
|
||||||
|
/// <summary>File representation.</summary>
|
||||||
|
public class File : Entry
|
||||||
|
{
|
||||||
|
private static UTF8Encoding _utfNoBOM = new UTF8Encoding(false, true);
|
||||||
|
|
||||||
|
/// <summary>Opens an existing file for reading.</summary>
|
||||||
|
/// <param name="forceLocal">Force usage of local file
|
||||||
|
/// system, to which directory is bound.</param>
|
||||||
|
/// <returns>A read-only <see cref="Stream"/> on the specified path.</returns>
|
||||||
|
public Stream OpenRead(bool forceLocal = false)
|
||||||
|
{
|
||||||
|
if (!forceLocal && FileSystem.RootFileSystem != null)
|
||||||
|
return FileSystem.RootFileSystem.FileOpenRead(FullPath);
|
||||||
|
else
|
||||||
|
return FileSystem.FileOpenRead(Path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Opens an existing file for writing.</summary>
|
||||||
|
/// <param name="forceLocal">Force usage of local file
|
||||||
|
/// system, to which directory is bound.</param>
|
||||||
|
/// <returns>An unshared <see cref="Stream"/> object on
|
||||||
|
/// the specified path with write access.</returns>
|
||||||
|
public Stream OpenWrite(bool forceLocal = false)
|
||||||
|
{
|
||||||
|
if (!forceLocal && FileSystem.RootFileSystem != null)
|
||||||
|
return FileSystem.RootFileSystem.FileOpenWrite(FullPath);
|
||||||
|
else
|
||||||
|
return FileSystem.FileOpenWrite(Path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Deletes the specified file. An exception is thrown
|
||||||
|
/// if the specified file does not exist.</summary>
|
||||||
|
/// <param name="forceLocal">Force usage of local file
|
||||||
|
/// system, to which directory is bound.</param>
|
||||||
|
public void Delete(bool forceLocal = false)
|
||||||
|
{
|
||||||
|
if (!forceLocal && FileSystem.RootFileSystem != null)
|
||||||
|
FileSystem.RootFileSystem.FileDelete(FullPath);
|
||||||
|
else
|
||||||
|
FileSystem.FileDelete(Path);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Utility
|
||||||
|
|
||||||
|
/// <summary>Opens a binary file, reads the contents of the file into a byte array, and then closes the file.</summary>
|
||||||
|
/// <returns>A byte array containing the contents of the file.</returns>
|
||||||
|
public byte[] ReadAllBytes()
|
||||||
|
{
|
||||||
|
byte[] array;
|
||||||
|
|
||||||
|
using (Stream fileStream = OpenRead(true))
|
||||||
|
{
|
||||||
|
int offset = 0;
|
||||||
|
long length = fileStream.Length;
|
||||||
|
if (length > 2147483647L)
|
||||||
|
throw new IOException("Can't read file larger than 2GB.");
|
||||||
|
|
||||||
|
int len = (int)length;
|
||||||
|
array = new byte[len];
|
||||||
|
|
||||||
|
while (len > 0)
|
||||||
|
{
|
||||||
|
int read = fileStream.Read(array, offset, len);
|
||||||
|
if (read == 0)
|
||||||
|
throw new EndOfStreamException("Can't read beyond file stream.");
|
||||||
|
|
||||||
|
offset += read;
|
||||||
|
len -= read;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Opens a file, reads all lines of the file with the specified encoding, and then closes the file.</summary>
|
||||||
|
/// <param name="encoding">The encoding applied to the contents of the file. </param>
|
||||||
|
/// <returns>A string array containing all lines of the file.</returns>
|
||||||
|
public string[] ReadAllLines(Encoding encoding = null)
|
||||||
|
{
|
||||||
|
if (encoding == null)
|
||||||
|
encoding = Encoding.UTF8;
|
||||||
|
|
||||||
|
List<string> list = new List<string>();
|
||||||
|
|
||||||
|
using (Stream fileStream = OpenRead(true))
|
||||||
|
using (StreamReader streamReader = new StreamReader(fileStream, encoding))
|
||||||
|
{
|
||||||
|
string item;
|
||||||
|
while ((item = streamReader.ReadLine()) != null)
|
||||||
|
{
|
||||||
|
list.Add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return list.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Opens a file, reads all lines of the file with the specified encoding, and then closes the file.</summary>
|
||||||
|
/// <param name="encoding">The encoding applied to the contents of the file. </param>
|
||||||
|
/// <returns>A string containing all lines of the file.</returns>
|
||||||
|
public string ReadAllText(Encoding encoding = null)
|
||||||
|
{
|
||||||
|
if (encoding == null)
|
||||||
|
encoding = Encoding.UTF8;
|
||||||
|
|
||||||
|
using (Stream fileStream = OpenRead(true))
|
||||||
|
using (StreamReader streamReader = new StreamReader(fileStream, encoding, true, 1024))
|
||||||
|
return streamReader.ReadToEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Writes the specified byte array to the file replacing it's current content,
|
||||||
|
/// and then closes the file.</summary>
|
||||||
|
/// <param name="bytes">The bytes to write to the file.</param>
|
||||||
|
public void WriteAllBytes(byte[] bytes)
|
||||||
|
{
|
||||||
|
using (Stream fileStream = FileSystem.FileCreate(Path))
|
||||||
|
fileStream.Write(bytes, 0, bytes.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Write the specified string array to the file replacing it's current
|
||||||
|
/// content, and then closes the file.</summary>
|
||||||
|
/// <param name="contents">The string array to write to the file.</param>
|
||||||
|
public void WriteAllLines(string[] contents)
|
||||||
|
{
|
||||||
|
using (Stream fileStream = FileSystem.FileCreate(Path))
|
||||||
|
using (var writer = new StreamWriter(fileStream, _utfNoBOM))
|
||||||
|
foreach (var item in contents)
|
||||||
|
writer.WriteLine(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Writes the specified string to the file replacing it's current content, and
|
||||||
|
/// then closes the file.</summary>
|
||||||
|
/// <param name="contents">The string to write to the file.</param>
|
||||||
|
public void WriteAllText(string contents)
|
||||||
|
{
|
||||||
|
using (Stream fileStream = FileSystem.FileCreate(Path))
|
||||||
|
using (var writer = new StreamWriter(fileStream, _utfNoBOM))
|
||||||
|
writer.Write(contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Opens a file, appends the specified byte array to
|
||||||
|
/// the file, and then closes the file.</summary>
|
||||||
|
/// <param name="bytes">The bytes to write to the file.</param>
|
||||||
|
public void AppendAllBytes(byte[] bytes)
|
||||||
|
{
|
||||||
|
using (Stream fileStream = OpenWrite(true))
|
||||||
|
fileStream.Write(bytes, 0, bytes.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Opens a file, appends the specified string array to
|
||||||
|
/// the file, and then closes the file.</summary>
|
||||||
|
/// <param name="contents">The string array to write to the file.</param>
|
||||||
|
public void AppendAllLines(string[] contents)
|
||||||
|
{
|
||||||
|
using (Stream fileStream = OpenWrite(true))
|
||||||
|
using (var writer = new StreamWriter(fileStream, _utfNoBOM))
|
||||||
|
foreach (var item in contents)
|
||||||
|
writer.WriteLine(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Opens a file, appends the specified string to the file, and
|
||||||
|
/// then closes the file.</summary>
|
||||||
|
/// <param name="contents">The string to write to the file.</param>
|
||||||
|
public void AppendAllText(string contents)
|
||||||
|
{
|
||||||
|
using (Stream fileStream = OpenWrite(true))
|
||||||
|
using (var writer = new StreamWriter(fileStream, _utfNoBOM))
|
||||||
|
writer.Write(contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Utility
|
||||||
|
}
|
||||||
|
}
|
||||||
149
VirtualFS/IFileSystem.cs
Normal file
149
VirtualFS/IFileSystem.cs
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
/*
|
||||||
|
* 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.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace VirtualFS
|
||||||
|
{
|
||||||
|
/// <summary>Describes basic operations on file system.</summary>
|
||||||
|
/// <remarks>All operations involving paths are considered
|
||||||
|
/// to include only local file system.</remarks>
|
||||||
|
public interface IFileSystem : IDisposable
|
||||||
|
{
|
||||||
|
/// <summary>Gets reference to root file system.</summary>
|
||||||
|
IRootFileSystem RootFileSystem { get; }
|
||||||
|
|
||||||
|
/// <summary>Gets or sets a value indicating whether this file system
|
||||||
|
/// has higher priority than others mounted on the same path.</summary>
|
||||||
|
/// <remarks>This value must be set corectly before mount.</remarks>
|
||||||
|
bool HighPriority { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Gets a value indicating whether this file system is read only.</summary>
|
||||||
|
bool IsReadOnly { get; }
|
||||||
|
|
||||||
|
/// <summary>Gets a value indicating whether this file system is busy.</summary>
|
||||||
|
/// <remarks>Implementations of file systems should set this flag if any
|
||||||
|
/// file is open in this file system.</remarks>
|
||||||
|
bool IsBusy { get; }
|
||||||
|
|
||||||
|
#region Common
|
||||||
|
|
||||||
|
/// <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>
|
||||||
|
bool Exists(Path path);
|
||||||
|
|
||||||
|
/// <summary>Get entry located under given path.</summary>
|
||||||
|
/// <param name="path">Path to get.</param>
|
||||||
|
/// <returns>Returns entry information.</returns>
|
||||||
|
Entry GetEntry(Path path);
|
||||||
|
|
||||||
|
/// <summary>Get path to physical file containing entry.</summary>
|
||||||
|
/// <remarks>This may not work with all file systems. Implementation
|
||||||
|
/// should return null if real path can't be determined.</remarks>
|
||||||
|
/// <param name="path">Virtual file system path.</param>
|
||||||
|
/// <returns>Real file system path or <c>null</c> if real
|
||||||
|
/// path doesn't exist.</returns>
|
||||||
|
string EntryRealPath(Path path);
|
||||||
|
|
||||||
|
/// <summary>Copies an existing file or directory to a new location.</summary>
|
||||||
|
/// <param name="source">Source path of file or directory.</param>
|
||||||
|
/// <param name="destination">Destination path. This path must ba a directory.</param>
|
||||||
|
/// <param name="overwrite">If <c>true</c> the destination files and directories
|
||||||
|
/// can be overwritten; otherwise, <c>false</c>.</param>
|
||||||
|
void Copy(Path source, Path destination, bool overwrite = false);
|
||||||
|
|
||||||
|
/// <summary>Moves an existing file or directory to a new location.</summary>
|
||||||
|
/// <param name="source">Source path of file or directory.</param>
|
||||||
|
/// <param name="destination">Destination path. This path must ba a directory.</param>
|
||||||
|
/// <param name="overwrite">If <c>true</c> the destination files and directories
|
||||||
|
/// can be overwritten; otherwise, <c>false</c>. Files that aren't moved
|
||||||
|
/// will remain on file system.</param>
|
||||||
|
/// <param name="leaveStructure">If <c>true</c> the source directories will be left intact;
|
||||||
|
/// otherwise, <c>false</c>.</param>
|
||||||
|
void Move(Path source, Path destination, bool overwrite = false, bool leaveStructure = false);
|
||||||
|
|
||||||
|
/// <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>
|
||||||
|
void ReName(Path path, string newName);
|
||||||
|
|
||||||
|
#endregion Common
|
||||||
|
|
||||||
|
#region Directory
|
||||||
|
|
||||||
|
/// <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>
|
||||||
|
IEnumerable<Entry> GetEntries(Path path, Regex mask = null);
|
||||||
|
|
||||||
|
/// <summary>Create directory and return new entry.</summary>
|
||||||
|
/// <remarks>Parent directory must exist.</remarks>
|
||||||
|
/// <param name="path">The directory path to create.</param>
|
||||||
|
/// <returns>Created entry.</returns>
|
||||||
|
Directory DirectoryCreate(Path 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>
|
||||||
|
void DirectoryDelete(Path path, bool recursive = false);
|
||||||
|
|
||||||
|
#endregion Directory
|
||||||
|
|
||||||
|
#region File
|
||||||
|
|
||||||
|
/// <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>
|
||||||
|
Stream FileOpenRead(Path path);
|
||||||
|
|
||||||
|
/// <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>
|
||||||
|
Stream FileOpenWrite(Path path);
|
||||||
|
|
||||||
|
/// <summary>Creates or overwrites a file in the specified path.</summary>
|
||||||
|
/// <param name="path">The path and name of the file to create.</param>
|
||||||
|
/// <returns>A <see cref="Stream"/> that provides
|
||||||
|
/// write access to the file specified in path.</returns>
|
||||||
|
Stream FileCreate(Path path);
|
||||||
|
|
||||||
|
/// <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>
|
||||||
|
void FileDelete(Path path);
|
||||||
|
|
||||||
|
#endregion File
|
||||||
|
}
|
||||||
|
}
|
||||||
69
VirtualFS/IRootFileSystem.cs
Normal file
69
VirtualFS/IRootFileSystem.cs
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* 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.Collections.Generic;
|
||||||
|
|
||||||
|
namespace VirtualFS
|
||||||
|
{
|
||||||
|
/// <summary>Interface describing root file system.</summary>
|
||||||
|
public interface IRootFileSystem : IFileSystem
|
||||||
|
{
|
||||||
|
/// <summary>Gets root of the file system.</summary>
|
||||||
|
Directory Root { get; }
|
||||||
|
|
||||||
|
/// <summary>Mount file system in given path.</summary>
|
||||||
|
/// <remarks>Mounting another file system can overlap, you
|
||||||
|
/// can mount several file systems in a given directory.
|
||||||
|
/// Mount path can't be a file. You can mount file system
|
||||||
|
/// in file system in file system and so on... When opening
|
||||||
|
/// a file which might exist on any of those file systems
|
||||||
|
/// last mounted is taken to consideration first.</remarks>
|
||||||
|
/// <param name="fs">File system to mount.</param>
|
||||||
|
/// <param name="path">Path on which mount new file system.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if file system was mounted
|
||||||
|
/// correctly, otherwise <c>false</c>.</returns>
|
||||||
|
bool Mount(IFileSystem fs, Path path);
|
||||||
|
|
||||||
|
/// <summary>Un mounts file system from current file system.</summary>
|
||||||
|
/// <remarks>All file systems mounted previously in path owned by
|
||||||
|
/// this file system will remain mounted and accessible.</remarks>
|
||||||
|
/// <param name="fs">File system to un mount.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if file system was un mounted
|
||||||
|
/// correctly, otherwise <c>false</c>.</returns>
|
||||||
|
bool Umount(IFileSystem fs);
|
||||||
|
|
||||||
|
/// <summary>Enumerate mounted file systems.</summary>
|
||||||
|
/// <returns>Enumeration of mounted file systems.</returns>
|
||||||
|
IEnumerable<IFileSystem> GetMounted();
|
||||||
|
|
||||||
|
/// <summary>Get mount path of this file system.</summary>
|
||||||
|
/// <param name="fileSystem">File system to check.</param>
|
||||||
|
/// <returns>Path to file system on root file system.</returns>
|
||||||
|
Path GetMountPath(IFileSystem fileSystem);
|
||||||
|
}
|
||||||
|
}
|
||||||
482
VirtualFS/Implementation/RootFileSystem.cs
Normal file
482
VirtualFS/Implementation/RootFileSystem.cs
Normal file
@@ -0,0 +1,482 @@
|
|||||||
|
/*
|
||||||
|
* 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.Text.RegularExpressions;
|
||||||
|
using VirtualFS.Base;
|
||||||
|
|
||||||
|
namespace VirtualFS.Implementation
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Root file system is a file system containing other file systems.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Root file system by it self is read only and uses '/' directory.
|
||||||
|
/// You can of course mount other file systems to root path.
|
||||||
|
/// </remarks>
|
||||||
|
public class RootFileSystem : BaseFileSystem, IRootFileSystem
|
||||||
|
{
|
||||||
|
private static object _globalLock = new object();
|
||||||
|
|
||||||
|
private Dictionary<Path, List<IFileSystem>> _mounts;
|
||||||
|
|
||||||
|
/// <summary>Initializes a new instance of the <see cref="RootFileSystem"/> class.</summary>
|
||||||
|
public RootFileSystem()
|
||||||
|
: base(true)
|
||||||
|
{
|
||||||
|
_mounts = new Dictionary<Path, List<IFileSystem>>();
|
||||||
|
|
||||||
|
Root = new Directory
|
||||||
|
{
|
||||||
|
FileSystem = this,
|
||||||
|
IsReadOnly = true,
|
||||||
|
Path = new Path(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#region IFileSystem implementation
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
// Find all filesystems containing this path
|
||||||
|
// If any of the file systems under this path
|
||||||
|
// contains file represented as shrter path, return true.
|
||||||
|
return _mounts
|
||||||
|
.Where(x => x.Key.IsParentOfOrSame(path))
|
||||||
|
.Any(x => x.Value.Any(f => f.Exists(path.RemoveParent(x.Key))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
// Find all filesystems containing this path
|
||||||
|
foreach (var kvp in _mounts
|
||||||
|
.Where(x => x.Key.IsParentOfOrSame(path)))
|
||||||
|
{
|
||||||
|
// Make path shorter (remove mount point)
|
||||||
|
var shortPath = path.RemoveParent(kvp.Key);
|
||||||
|
|
||||||
|
// If find first file system that contains
|
||||||
|
// this file.
|
||||||
|
var fs = kvp.Value.FirstOrDefault(f => f.Exists(shortPath));
|
||||||
|
|
||||||
|
// If file system found open file
|
||||||
|
if (fs != null)
|
||||||
|
return fs.GetEntry(shortPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sorry file not found
|
||||||
|
throw new System.IO.FileNotFoundException(string.Format("File '{0}' was not found under any of the mounted file systems.", path), path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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>
|
||||||
|
/// <exception cref="System.NotImplementedException">File system
|
||||||
|
/// containing parent directory was not found.</exception>
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
Path parent = path.Parent;
|
||||||
|
|
||||||
|
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.Exists(parent.RemoveParent(pfs.Path)));
|
||||||
|
|
||||||
|
if (pathAndFs == null)
|
||||||
|
throw new InvalidOperationException("File system containing parent directory was not found.");
|
||||||
|
|
||||||
|
return pathAndFs.FileSystem.EntryRealPath(path.RemoveParent(parent));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Copies an existing file or directory to a new location.</summary>
|
||||||
|
/// <param name="source">Source path of file or directory.</param>
|
||||||
|
/// <param name="destination">Destination path. This path must ba a directory.</param>
|
||||||
|
/// <param name="overwrite">If <c>true</c> the destination files and directories
|
||||||
|
/// can be overwritten; otherwise, <c>false</c>.</param>
|
||||||
|
/// <remarks>This method uses simple yet universal approach.
|
||||||
|
/// it's much slower than native one so implementing file system consider
|
||||||
|
/// better approach for internal copy.</remarks>
|
||||||
|
public override void Copy(Path source, Path destination, bool overwrite = false)
|
||||||
|
{
|
||||||
|
Path srcParent = source.Parent;
|
||||||
|
Path dstParent = destination.Parent;
|
||||||
|
|
||||||
|
var pathAndFsSrc = _mounts
|
||||||
|
.Where(x => x.Key.IsParentOfOrSame(srcParent))
|
||||||
|
.SelectMany(v => v.Value, (k, v) => new { Path = k.Key, FileSystem = v })
|
||||||
|
.FirstOrDefault(pfs => pfs.FileSystem.Exists(srcParent.RemoveParent(pfs.Path)));
|
||||||
|
|
||||||
|
var pathAndFsDst = _mounts
|
||||||
|
.Where(x => x.Key.IsParentOfOrSame(dstParent))
|
||||||
|
.SelectMany(v => v.Value, (k, v) => new { Path = k.Key, FileSystem = v })
|
||||||
|
.FirstOrDefault(pfs => !pfs.FileSystem.IsReadOnly && pfs.FileSystem.Exists(dstParent.RemoveParent(pfs.Path)));
|
||||||
|
|
||||||
|
if (pathAndFsSrc != null && pathAndFsDst != null &&
|
||||||
|
pathAndFsSrc.Path == pathAndFsDst.Path &&
|
||||||
|
pathAndFsSrc.FileSystem == pathAndFsDst.FileSystem)
|
||||||
|
{
|
||||||
|
pathAndFsSrc.FileSystem.Copy(source, destination, overwrite);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
base.Copy(source, destination, overwrite);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Moves an existing file or directory to a new location.</summary>
|
||||||
|
/// <param name="source">Source path of file or directory.</param>
|
||||||
|
/// <param name="destination">Destination path. This path must ba a directory.</param>
|
||||||
|
/// <param name="overwrite">If <c>true</c> the destination files and directories
|
||||||
|
/// can be overwritten; otherwise, <c>false</c>. Files that aren't moved
|
||||||
|
/// will remain on file system.</param>
|
||||||
|
/// <param name="leaveStructure">If <c>true</c> the source directories will be left intact;
|
||||||
|
/// otherwise, <c>false</c>.</param>
|
||||||
|
/// <remarks>This method uses simple yet universal approach.
|
||||||
|
/// it's much slower than native one so implementing file system consider
|
||||||
|
/// better approach for internal move.</remarks>
|
||||||
|
public override void Move(Path source, Path destination, bool overwrite = false, bool leaveStructure = false)
|
||||||
|
{
|
||||||
|
Path srcParent = source.Parent;
|
||||||
|
Path dstParent = destination.Parent;
|
||||||
|
|
||||||
|
var pathAndFsSrc = _mounts
|
||||||
|
.Where(x => x.Key.IsParentOfOrSame(srcParent))
|
||||||
|
.SelectMany(v => v.Value, (k, v) => new { Path = k.Key, FileSystem = v })
|
||||||
|
.FirstOrDefault(pfs => !pfs.FileSystem.IsReadOnly && pfs.FileSystem.Exists(srcParent.RemoveParent(pfs.Path)));
|
||||||
|
|
||||||
|
var pathAndFsDst = _mounts
|
||||||
|
.Where(x => x.Key.IsParentOfOrSame(dstParent))
|
||||||
|
.SelectMany(v => v.Value, (k, v) => new { Path = k.Key, FileSystem = v })
|
||||||
|
.FirstOrDefault(pfs => !pfs.FileSystem.IsReadOnly && pfs.FileSystem.Exists(dstParent.RemoveParent(pfs.Path)));
|
||||||
|
|
||||||
|
if (pathAndFsSrc != null && pathAndFsDst != null &&
|
||||||
|
pathAndFsSrc.Path == pathAndFsDst.Path &&
|
||||||
|
pathAndFsSrc.FileSystem == pathAndFsDst.FileSystem)
|
||||||
|
{
|
||||||
|
pathAndFsSrc.FileSystem.Move(source, destination, overwrite);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
base.Move(source, destination, overwrite, leaveStructure);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
if (path.Name == newName)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Path parent = path.Parent;
|
||||||
|
|
||||||
|
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.");
|
||||||
|
|
||||||
|
pathAndFs.FileSystem.ReName(path.RemoveParent(parent), newName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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."));
|
||||||
|
|
||||||
|
return _mounts
|
||||||
|
.Where(x => x.Key.IsParentOfOrSame(path))
|
||||||
|
.SelectMany(v => v.Value.Where(x => x.Exists(path.RemoveParent(v.Key))),
|
||||||
|
(k, v) => v.GetEntries(path.RemoveParent(k.Key)))
|
||||||
|
.SelectMany(e => e, (p, e) => e)
|
||||||
|
.Distinct();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Create Directory and return new entry.</summary>
|
||||||
|
/// <param name="path">Path of directory.</param>
|
||||||
|
/// <returns>Created entry.</returns>
|
||||||
|
/// <exception cref="System.InvalidOperationException">Can't create root directory.
|
||||||
|
/// or Writable file system containing parent directory was not found.</exception>
|
||||||
|
public override Directory DirectoryCreate(Path path)
|
||||||
|
{
|
||||||
|
Path parent = path.Parent;
|
||||||
|
|
||||||
|
if (parent == null)
|
||||||
|
throw new InvalidOperationException("Can't create root directory.");
|
||||||
|
|
||||||
|
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.DirectoryCreate(path.RemoveParent(parent));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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>
|
||||||
|
/// <exception cref="System.InvalidOperationException">
|
||||||
|
/// Writable file system containing parent directory was not found.
|
||||||
|
/// </exception>
|
||||||
|
public override void DirectoryDelete(Path path, bool recursive = false)
|
||||||
|
{
|
||||||
|
Path parent = path.Parent;
|
||||||
|
|
||||||
|
if (parent == null)
|
||||||
|
throw new InvalidOperationException(string.Format("Can't delete root directory.", 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.");
|
||||||
|
|
||||||
|
pathAndFs.FileSystem.DirectoryDelete(path.RemoveParent(parent));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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>
|
||||||
|
/// <exception cref="System.InvalidOperationException">File
|
||||||
|
/// system containing parent directory was not found.</exception>
|
||||||
|
public override Stream FileOpenRead(Path path)
|
||||||
|
{
|
||||||
|
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.Exists(parent.RemoveParent(pfs.Path)));
|
||||||
|
|
||||||
|
if (pathAndFs == null)
|
||||||
|
throw new InvalidOperationException("File system containing parent directory was not found.");
|
||||||
|
|
||||||
|
return pathAndFs.FileSystem.FileOpenRead(path.RemoveParent(parent));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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>
|
||||||
|
/// <exception cref="System.InvalidOperationException">Writable
|
||||||
|
/// file system containing parent directory was not found.</exception>
|
||||||
|
public override Stream FileOpenWrite(Path path)
|
||||||
|
{
|
||||||
|
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.FileOpenWrite(path.RemoveParent(parent));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Creates or overwrites a file in the specified path.</summary>
|
||||||
|
/// <param name="path">The path and name of the file to create.</param>
|
||||||
|
/// <returns>A <see cref="Stream" /> that provides
|
||||||
|
/// write access to the file specified in path.</returns>
|
||||||
|
/// <exception cref="System.InvalidOperationException">Writable
|
||||||
|
/// file system containing parent directory was not found.</exception>
|
||||||
|
public override Stream FileCreate(Path path)
|
||||||
|
{
|
||||||
|
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.FileCreate(path.RemoveParent(parent));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Files the delete.</summary>
|
||||||
|
/// <param name="path">The path.</param>
|
||||||
|
/// <exception cref="System.InvalidOperationException">Writable
|
||||||
|
/// file system containing parent directory was not found.</exception>
|
||||||
|
public override void FileDelete(Path path)
|
||||||
|
{
|
||||||
|
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.");
|
||||||
|
|
||||||
|
pathAndFs.FileSystem.FileDelete(path.RemoveParent(parent));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion IFileSystem implementation
|
||||||
|
|
||||||
|
#region IRootFileSystem implementation
|
||||||
|
|
||||||
|
/// <summary>Gets root of the file system.</summary>
|
||||||
|
public Directory Root { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>Mount file system in given path.</summary>
|
||||||
|
/// <param name="fs">File system to mount. This should be
|
||||||
|
/// implementation of <see cref="BaseFileSystem"/>.</param>
|
||||||
|
/// <param name="path">Path on which mount new file system.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if file system was mounted
|
||||||
|
/// correctly, otherwise <c>false</c>.</returns>
|
||||||
|
/// <exception cref="System.InvalidOperationException">
|
||||||
|
/// Path '{0}' is not a directory. You can only mount
|
||||||
|
/// file system under directories. or File system is
|
||||||
|
/// already mounted.</exception>
|
||||||
|
/// <remarks>Mounting another file system can overlap, you
|
||||||
|
/// can mount several file systems in a given directory.
|
||||||
|
/// Mount path can't be a file. You can mount file system
|
||||||
|
/// in file system in file system and so on... When opening
|
||||||
|
/// a file which might exist on any of those file systems
|
||||||
|
/// last mounted is taken to consideration first.</remarks>
|
||||||
|
public bool Mount(IFileSystem fs, Path path)
|
||||||
|
{
|
||||||
|
if (!path.IsDirectory)
|
||||||
|
throw new InvalidOperationException("Path '{0}' is not a directory. You can only mount file system under directories.");
|
||||||
|
|
||||||
|
lock (_globalLock)
|
||||||
|
{
|
||||||
|
if (GetMounted().Contains(fs))
|
||||||
|
throw new InvalidOperationException("File system is already mounted.");
|
||||||
|
|
||||||
|
if (!_mounts.ContainsKey(path))
|
||||||
|
_mounts.Add(path, new List<IFileSystem>());
|
||||||
|
|
||||||
|
_mounts[path].Insert(_mounts[path].Count(f => f.HighPriority), fs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Un mounts file system from current file system.</summary>
|
||||||
|
/// <param name="fs">File system to un mount.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if file system was un mounted
|
||||||
|
/// correctly, otherwise <c>false</c>.</returns>
|
||||||
|
/// <remarks>All file systems mounted previously in path owned by
|
||||||
|
/// this file system will remain mounted and accessible.</remarks>
|
||||||
|
public bool Umount(IFileSystem fs)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
|
||||||
|
// If file system is busy fail.
|
||||||
|
if (!fs.IsBusy)
|
||||||
|
{
|
||||||
|
// Find all mount paths
|
||||||
|
var mountPaths = _mounts
|
||||||
|
.Where(x => x.Value.Contains(fs))
|
||||||
|
.Select(x => x.Key)
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
int num = mountPaths.Length;
|
||||||
|
|
||||||
|
// If file system is not mounted fail.
|
||||||
|
if (num > 0)
|
||||||
|
for (int i = 0; i < num; i++)
|
||||||
|
{
|
||||||
|
_mounts[mountPaths[i]].Remove(fs);
|
||||||
|
|
||||||
|
if (_mounts[mountPaths[i]].Count == 0)
|
||||||
|
_mounts.Remove(mountPaths[i]);
|
||||||
|
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Enumerate mounted file systems.</summary>
|
||||||
|
/// <returns>Enumeration of mounted file systems.</returns>
|
||||||
|
public IEnumerable<IFileSystem> GetMounted()
|
||||||
|
{
|
||||||
|
return _mounts
|
||||||
|
.Values
|
||||||
|
.SelectMany(l => l);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Get mount path of this file system.</summary>
|
||||||
|
/// <param name="fileSystem">File system to check.</param>
|
||||||
|
/// <returns>Path to file system on root file system.</returns>
|
||||||
|
public Path GetMountPath(IFileSystem fileSystem)
|
||||||
|
{
|
||||||
|
var pfs = _mounts
|
||||||
|
.SelectMany(k => k.Value, (k, v) => new { Path = k.Key, FileSystem = v })
|
||||||
|
.SingleOrDefault(f => f.FileSystem == fileSystem);
|
||||||
|
|
||||||
|
return pfs == null ? null : pfs.Path;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion IRootFileSystem implementation
|
||||||
|
}
|
||||||
|
}
|
||||||
251
VirtualFS/Implementation/VirtualStream.cs
Normal file
251
VirtualFS/Implementation/VirtualStream.cs
Normal file
@@ -0,0 +1,251 @@
|
|||||||
|
/*
|
||||||
|
* 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.IO;
|
||||||
|
using VirtualFS.Base;
|
||||||
|
|
||||||
|
namespace VirtualFS.Implementation
|
||||||
|
{
|
||||||
|
/// <summary>Provides a generic view of a sequence
|
||||||
|
/// of bytes in virtual file system.</summary>
|
||||||
|
public class VirtualStream : Stream
|
||||||
|
{
|
||||||
|
/// <summary>Gets the internal stream.</summary>
|
||||||
|
public Stream InternalStream { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>Gets the file system.</summary>
|
||||||
|
public IFileSystem FileSystem { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>Initializes a new instance of
|
||||||
|
/// the <see cref="VirtualStream" /> class.</summary>
|
||||||
|
/// <param name="fileSystem">The file system.</param>
|
||||||
|
/// <param name="stream">The internal stream.</param>
|
||||||
|
internal VirtualStream(BaseFileSystem fileSystem, Stream stream)
|
||||||
|
{
|
||||||
|
fileSystem.IncreaseOpened();
|
||||||
|
InternalStream = stream;
|
||||||
|
FileSystem = fileSystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Closes the current stream and releases any resources
|
||||||
|
/// (such as sockets and file handles) associated with the current
|
||||||
|
/// stream.</summary>
|
||||||
|
/// <remarks>This wrapped method will decrease number of opened
|
||||||
|
/// files in file system.</remarks>
|
||||||
|
public override void Close()
|
||||||
|
{
|
||||||
|
((BaseFileSystem)FileSystem).DecreaseOpened();
|
||||||
|
InternalStream.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Wrapped Properties
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When overridden in a derived class, gets a value indicating whether the current stream supports reading.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>true if the stream supports reading; otherwise, false.</returns>
|
||||||
|
public override bool CanRead { get { return InternalStream.CanRead; } }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When overridden in a derived class, gets a value indicating whether the current stream supports seeking.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>true if the stream supports seeking; otherwise, false.</returns>
|
||||||
|
public override bool CanSeek { get { return InternalStream.CanSeek; } }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value that determines whether the current stream can time out.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A value that determines whether the current stream can time out.</returns>
|
||||||
|
public override bool CanTimeout { get { return InternalStream.CanTimeout; } }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When overridden in a derived class, gets a value indicating whether the current stream supports writing.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>true if the stream supports writing; otherwise, false.</returns>
|
||||||
|
public override bool CanWrite { get { return InternalStream.CanWrite; } }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When overridden in a derived class, gets the length in bytes of the stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A long value representing the length of the stream in bytes.</returns>
|
||||||
|
public override long Length { get { return InternalStream.Length; } }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When overridden in a derived class, gets or sets the position within the current stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The current position within the stream.</returns>
|
||||||
|
public override long Position { get { return InternalStream.Position; } set { InternalStream.Position = value; } }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value, in milliseconds, that determines how long the stream will attempt to read before timing out.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A value, in milliseconds, that determines how long the stream will attempt to read before timing out.</returns>
|
||||||
|
public override int ReadTimeout { get { return InternalStream.ReadTimeout; } set { InternalStream.ReadTimeout = value; } }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value, in milliseconds, that determines how long the stream will attempt to write before timing out.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A value, in milliseconds, that determines how long the stream will attempt to write before timing out.</returns>
|
||||||
|
public override int WriteTimeout { get { return InternalStream.WriteTimeout; } set { InternalStream.WriteTimeout = value; } }
|
||||||
|
|
||||||
|
#endregion Wrapped Properties
|
||||||
|
|
||||||
|
#region Wrapped Methods
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Begins an asynchronous read operation.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="buffer">The buffer to read the data into.</param>
|
||||||
|
/// <param name="offset">The byte offset in <paramref name="buffer" /> at which to begin writing data read from the stream.</param>
|
||||||
|
/// <param name="count">The maximum number of bytes to read.</param>
|
||||||
|
/// <param name="callback">An optional asynchronous callback, to be called when the read is complete.</param>
|
||||||
|
/// <param name="state">A user-provided object that distinguishes this particular asynchronous read request from other requests.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// An <see cref="T:System.IAsyncResult" /> that represents the asynchronous read, which could still be pending.
|
||||||
|
/// </returns>
|
||||||
|
public override System.IAsyncResult BeginRead(byte[] buffer, int offset, int count, System.AsyncCallback callback, object state)
|
||||||
|
{
|
||||||
|
return InternalStream.BeginRead(buffer, offset, count, callback, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Begins an asynchronous write operation.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="buffer">The buffer to write data from.</param>
|
||||||
|
/// <param name="offset">The byte offset in <paramref name="buffer" /> from which to begin writing.</param>
|
||||||
|
/// <param name="count">The maximum number of bytes to write.</param>
|
||||||
|
/// <param name="callback">An optional asynchronous callback, to be called when the write is complete.</param>
|
||||||
|
/// <param name="state">A user-provided object that distinguishes this particular asynchronous write request from other requests.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// An IAsyncResult that represents the asynchronous write, which could still be pending.
|
||||||
|
/// </returns>
|
||||||
|
public override System.IAsyncResult BeginWrite(byte[] buffer, int offset, int count, System.AsyncCallback callback, object state)
|
||||||
|
{
|
||||||
|
return InternalStream.BeginWrite(buffer, offset, count, callback, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Waits for the pending asynchronous read to complete.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="asyncResult">The reference to the pending asynchronous request to finish.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// The number of bytes read from the stream, between zero (0) and the number of bytes you requested. Streams return zero (0) only at the end of the stream, otherwise, they should block until at least one byte is available.
|
||||||
|
/// </returns>
|
||||||
|
public override int EndRead(System.IAsyncResult asyncResult)
|
||||||
|
{
|
||||||
|
return InternalStream.EndRead(asyncResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ends an asynchronous write operation.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="asyncResult">A reference to the outstanding asynchronous I/O request.</param>
|
||||||
|
public override void EndWrite(System.IAsyncResult asyncResult)
|
||||||
|
{
|
||||||
|
InternalStream.EndWrite(asyncResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When overridden in a derived class, clears all buffers for this
|
||||||
|
/// stream and causes any buffered data to be written to the underlying device.
|
||||||
|
/// </summary>
|
||||||
|
public override void Flush()
|
||||||
|
{
|
||||||
|
InternalStream.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When overridden in a derived class, reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="buffer">An array of bytes. When this method returns, the buffer contains the specified byte array with the values between <paramref name="offset" /> and (<paramref name="offset" /> + <paramref name="count" /> - 1) replaced by the bytes read from the current source.</param>
|
||||||
|
/// <param name="offset">The zero-based byte offset in <paramref name="buffer" /> at which to begin storing the data read from the current stream.</param>
|
||||||
|
/// <param name="count">The maximum number of bytes to be read from the current stream.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached.
|
||||||
|
/// </returns>
|
||||||
|
public override int Read(byte[] buffer, int offset, int count)
|
||||||
|
{
|
||||||
|
return InternalStream.Read(buffer, offset, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a byte from the stream and advances the position within the stream by one byte, or returns -1 if at the end of the stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// The unsigned byte cast to an Int32, or -1 if at the end of the stream.
|
||||||
|
/// </returns>
|
||||||
|
public override int ReadByte()
|
||||||
|
{
|
||||||
|
return InternalStream.ReadByte();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When overridden in a derived class, sets the position within the current stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="offset">A byte offset relative to the <paramref name="origin" /> parameter.</param>
|
||||||
|
/// <param name="origin">A value of type <see cref="T:System.IO.SeekOrigin" /> indicating the reference point used to obtain the new position.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// The new position within the current stream.
|
||||||
|
/// </returns>
|
||||||
|
public override long Seek(long offset, SeekOrigin origin)
|
||||||
|
{
|
||||||
|
return InternalStream.Seek(offset, origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When overridden in a derived class, sets the length of the current stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The desired length of the current stream in bytes.</param>
|
||||||
|
public override void SetLength(long value)
|
||||||
|
{
|
||||||
|
InternalStream.SetLength(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When overridden in a derived class, writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="buffer">An array of bytes. This method copies <paramref name="count" /> bytes from <paramref name="buffer" /> to the current stream.</param>
|
||||||
|
/// <param name="offset">The zero-based byte offset in <paramref name="buffer" /> at which to begin copying bytes to the current stream.</param>
|
||||||
|
/// <param name="count">The number of bytes to be written to the current stream.</param>
|
||||||
|
public override void Write(byte[] buffer, int offset, int count)
|
||||||
|
{
|
||||||
|
InternalStream.Write(buffer, offset, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes a byte to the current position in the stream and advances the position within the stream by one byte.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The byte to write to the stream.</param>
|
||||||
|
public override void WriteByte(byte value)
|
||||||
|
{
|
||||||
|
base.WriteByte(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Wrapped Methods
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
VirtualFS/Libraries/ICSharpCode.SharpZipLib.dll
Normal file
BIN
VirtualFS/Libraries/ICSharpCode.SharpZipLib.dll
Normal file
Binary file not shown.
265
VirtualFS/Memory/MemoryFileSystem.cs
Normal file
265
VirtualFS/Memory/MemoryFileSystem.cs
Normal file
@@ -0,0 +1,265 @@
|
|||||||
|
/*
|
||||||
|
* 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.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using VirtualFS.Base;
|
||||||
|
using VirtualFS.Implementation;
|
||||||
|
|
||||||
|
namespace VirtualFS.Memory
|
||||||
|
{
|
||||||
|
/// <summary>In memory file system implementation.</summary>
|
||||||
|
public class MemoryFileSystem : BaseFileSystem
|
||||||
|
{
|
||||||
|
private static object _lock = new object();
|
||||||
|
private Dictionary<Path, Entry> _pathToEntry;
|
||||||
|
|
||||||
|
/// <summary>Initializes a new instance of the <see cref="MemoryFileSystem"/> class.</summary>
|
||||||
|
public MemoryFileSystem()
|
||||||
|
: base(false)
|
||||||
|
{
|
||||||
|
_pathToEntry = new Dictionary<Path, Entry>
|
||||||
|
{
|
||||||
|
{
|
||||||
|
Path.Root, new Directory
|
||||||
|
{
|
||||||
|
Path = Path.Root,
|
||||||
|
Data = null,
|
||||||
|
FileSystem = this,
|
||||||
|
IsReadOnly = IsReadOnly,
|
||||||
|
CreationTime = DateTime.Now,
|
||||||
|
LastAccessTime = DateTime.Now,
|
||||||
|
LastWriteTime = DateTime.Now,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
|
||||||
|
lock (_lock)
|
||||||
|
ret = path == Path.Root ||
|
||||||
|
(_pathToEntry.ContainsKey(path) &&
|
||||||
|
((_pathToEntry[path] is Directory) == path.IsDirectory));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
if (!Exists(path))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
Entry e = null;
|
||||||
|
|
||||||
|
lock (_lock)
|
||||||
|
e = _pathToEntry[path];
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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);
|
||||||
|
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
var e = _pathToEntry[path];
|
||||||
|
|
||||||
|
Path newPath = path.Parent + newName;
|
||||||
|
e.Path = newPath;
|
||||||
|
|
||||||
|
_pathToEntry[newPath] = e;
|
||||||
|
_pathToEntry.Remove(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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);
|
||||||
|
|
||||||
|
var dir = new Directory
|
||||||
|
{
|
||||||
|
Path = path,
|
||||||
|
Data = null,
|
||||||
|
FileSystem = this,
|
||||||
|
IsReadOnly = IsReadOnly,
|
||||||
|
CreationTime = DateTime.Now,
|
||||||
|
LastAccessTime = DateTime.Now,
|
||||||
|
LastWriteTime = DateTime.Now,
|
||||||
|
};
|
||||||
|
|
||||||
|
_pathToEntry[path] = dir;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
lock (_lock)
|
||||||
|
_pathToEntry.Keys
|
||||||
|
.Where(x => x.IsParentOfOrSame(path))
|
||||||
|
.ToList()
|
||||||
|
.ForEach(x =>
|
||||||
|
{
|
||||||
|
_pathToEntry[x].Data = null;
|
||||||
|
_pathToEntry.Remove(x);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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."));
|
||||||
|
|
||||||
|
IEnumerable<Entry> list = null;
|
||||||
|
|
||||||
|
if (Exists(path))
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
list = _pathToEntry
|
||||||
|
.Where(k => k.Key.Parent == path)
|
||||||
|
.Where(k => mask == null || mask.IsMatch(k.Key.Name))
|
||||||
|
.Select(v => v.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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 System.IO.Stream FileOpenRead(Path path)
|
||||||
|
{
|
||||||
|
base.FileOpenRead(path);
|
||||||
|
|
||||||
|
return new VirtualStream(this, new System.IO.MemoryStream((byte[])(GetEntry(path).Data ?? new byte[] { })));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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 override System.IO.Stream FileOpenWrite(Path path)
|
||||||
|
{
|
||||||
|
base.FileOpenWrite(path);
|
||||||
|
|
||||||
|
var e = GetEntry(path);
|
||||||
|
var data = (byte[])(e.Data ?? new byte[] { });
|
||||||
|
|
||||||
|
var ms = new System.IO.MemoryStream();
|
||||||
|
ms.Write(data, 0, data.Length);
|
||||||
|
|
||||||
|
return new MemoryWriteVirtualStream(this, (File)e, ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Creates or overwrites a file in the specified path.</summary>
|
||||||
|
/// <param name="path">The path and name of the file to create.</param>
|
||||||
|
/// <returns>A <see cref="Stream" /> that provides
|
||||||
|
/// write access to the file specified in path.</returns>
|
||||||
|
public override System.IO.Stream FileCreate(Path path)
|
||||||
|
{
|
||||||
|
base.FileCreate(path);
|
||||||
|
|
||||||
|
var e = (File)GetEntry(path);
|
||||||
|
if (e != null)
|
||||||
|
e.Data = new byte[] { };
|
||||||
|
else
|
||||||
|
{
|
||||||
|
e = new File
|
||||||
|
{
|
||||||
|
Path = path,
|
||||||
|
Data = new byte[] { },
|
||||||
|
FileSystem = this,
|
||||||
|
IsReadOnly = IsReadOnly,
|
||||||
|
CreationTime = DateTime.Now,
|
||||||
|
LastAccessTime = DateTime.Now,
|
||||||
|
LastWriteTime = DateTime.Now,
|
||||||
|
};
|
||||||
|
|
||||||
|
lock (_lock)
|
||||||
|
_pathToEntry[path] = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new MemoryWriteVirtualStream(this, e, new System.IO.MemoryStream());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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);
|
||||||
|
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
_pathToEntry[path].Data = null;
|
||||||
|
_pathToEntry.Remove(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
65
VirtualFS/Memory/MemoryWriteVirtualStream.cs
Normal file
65
VirtualFS/Memory/MemoryWriteVirtualStream.cs
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* 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.IO;
|
||||||
|
using VirtualFS.Base;
|
||||||
|
using VirtualFS.Implementation;
|
||||||
|
|
||||||
|
namespace VirtualFS.Memory
|
||||||
|
{
|
||||||
|
/// <summary>Provides a view of a sequence
|
||||||
|
/// of bytes in virtual file system that is
|
||||||
|
/// related to in memory file entry.</summary>
|
||||||
|
public class MemoryWriteVirtualStream : VirtualStream
|
||||||
|
{
|
||||||
|
/// <summary>Gets the entry associated with this stream.</summary>
|
||||||
|
public File File { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>Initializes a new instance of the
|
||||||
|
/// <see cref="MemoryWriteVirtualStream" /> class.</summary>
|
||||||
|
/// <param name="fileSystem">The file system.</param>
|
||||||
|
/// <param name="file">The file to which stream belongs.</param>
|
||||||
|
/// <param name="stream">The internal stream.</param>
|
||||||
|
internal MemoryWriteVirtualStream(BaseFileSystem fileSystem, File file, MemoryStream stream)
|
||||||
|
: base(fileSystem, stream)
|
||||||
|
{
|
||||||
|
File = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Closes the current stream and releases any resources
|
||||||
|
/// (such as sockets and file handles) associated with the current
|
||||||
|
/// stream.</summary>
|
||||||
|
/// <remarks>This wrapped method will decrease number of opened
|
||||||
|
/// files in file system.</remarks>
|
||||||
|
public override void Close()
|
||||||
|
{
|
||||||
|
File.Data = ((MemoryStream)InternalStream).ToArray();
|
||||||
|
base.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
307
VirtualFS/Path.cs
Normal file
307
VirtualFS/Path.cs
Normal file
@@ -0,0 +1,307 @@
|
|||||||
|
/*
|
||||||
|
* 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.Linq;
|
||||||
|
using VirtualFS.Extensions;
|
||||||
|
|
||||||
|
namespace VirtualFS
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Class representing path in file system.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Path is always rooted, so you can't make a mistake.
|
||||||
|
/// </remarks>
|
||||||
|
public class Path
|
||||||
|
{
|
||||||
|
#region Static part
|
||||||
|
|
||||||
|
/// <summary>Gets char used to split path elements.</summary>
|
||||||
|
public static char SeparatorChar { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>Gets char used to file name from it's extension.</summary>
|
||||||
|
public static char ExtensionChar { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>Gets an array with some invalid characters that
|
||||||
|
/// cannot be used with path names.</summary>
|
||||||
|
public static char[] InvalidPathChars { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>Gets an array with some invalid sequences that
|
||||||
|
/// cannot be used with path names.</summary>
|
||||||
|
public static string[] InvalidPathSequences { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>Gets an array with some invalid characters
|
||||||
|
/// that cannot be used file names.</summary>
|
||||||
|
public static char[] InvalidFileChars { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>Gets root path.</summary>
|
||||||
|
public static Path Root { get; private set; }
|
||||||
|
|
||||||
|
static Path()
|
||||||
|
{
|
||||||
|
string invalidPathChars = "\"<>|\0:*?\\";
|
||||||
|
|
||||||
|
SeparatorChar = '/';
|
||||||
|
ExtensionChar = '.';
|
||||||
|
InvalidPathSequences = new string[] { SeparatorChar.ToString() + SeparatorChar };
|
||||||
|
InvalidPathChars = invalidPathChars.ToCharArray();
|
||||||
|
InvalidFileChars = (SeparatorChar + invalidPathChars).ToCharArray();
|
||||||
|
|
||||||
|
Root = new Path();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Static part
|
||||||
|
|
||||||
|
private string _path;
|
||||||
|
private string[] _segments;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="Path"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public Path()
|
||||||
|
: this(SeparatorChar.ToString())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="Path"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The path.</param>
|
||||||
|
/// <exception cref="System.InvalidOperationException">Path is not rooted.</exception>
|
||||||
|
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)
|
||||||
|
throw new InvalidOperationException("Path is not rooted.");
|
||||||
|
|
||||||
|
IsDirectory = _path.EndsWith(SeparatorChar.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Gets a value indicating whether this instance is directory.</summary>
|
||||||
|
public virtual bool IsDirectory { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>Appends the specified path.</summary>
|
||||||
|
/// <param name="path">The path.</param>
|
||||||
|
/// <returns>New instance path combined from parent and new name.</returns>
|
||||||
|
/// <exception cref="System.InvalidOperationException">
|
||||||
|
/// The specified path is not a directory. or Added path is rooted.</exception>
|
||||||
|
public Path Append(string path)
|
||||||
|
{
|
||||||
|
if (!IsDirectory)
|
||||||
|
throw new InvalidOperationException("The specified path is not a directory.");
|
||||||
|
|
||||||
|
if (path[0] == SeparatorChar)
|
||||||
|
throw new InvalidOperationException("Added path is rooted.");
|
||||||
|
|
||||||
|
return new Path(_path + path.Validated("Path", invalidChars: InvalidPathChars, invalidSeq: InvalidPathSequences));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Determines whether specified path is same as current
|
||||||
|
/// path or this path is parent of given path.</summary>
|
||||||
|
/// <param name="path">The path.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if specified path is same as current
|
||||||
|
/// path or this path is parent of given path otherwise, <c>false</c>.</returns>
|
||||||
|
public bool IsParentOfOrSame(Path path)
|
||||||
|
{
|
||||||
|
return this == path || IsParentOf(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Determines whether this path is parent of given path.</summary>
|
||||||
|
/// <param name="path">The path.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if this path is parent of given path; otherwise, <c>false</c>.</returns>
|
||||||
|
public bool IsParentOf(Path path)
|
||||||
|
{
|
||||||
|
return IsDirectory && _path.Length != path._path.Length && path._path.StartsWith(_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Determines whether this path is child of given path.</summary>
|
||||||
|
/// <param name="path">The path.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if this path is child of given path, <c>false</c>.</returns>
|
||||||
|
public bool IsChildOf(Path path)
|
||||||
|
{
|
||||||
|
return path.IsParentOf(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Gets the file extension.</summary>
|
||||||
|
/// <returns>Return extension string.</returns>
|
||||||
|
/// <exception cref="System.ArgumentException">The specified path is not a file.</exception>
|
||||||
|
public string GetExtension()
|
||||||
|
{
|
||||||
|
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(extensionIndex + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Gets the name of entry from path.</summary>
|
||||||
|
public string Name { get { return _segments.Length > 0 ? _segments.Last() : null; } }
|
||||||
|
|
||||||
|
/// <summary>Gets the parent path.</summary>
|
||||||
|
public Path Parent { get { return GetParent(); } }
|
||||||
|
|
||||||
|
private Path GetParent()
|
||||||
|
{
|
||||||
|
switch (_segments.Length)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
|
||||||
|
// If root, then there is no parent
|
||||||
|
return null;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
|
||||||
|
// If only one segment then parent is root
|
||||||
|
return new Path();
|
||||||
|
|
||||||
|
default:
|
||||||
|
|
||||||
|
// Join segments with separator and soround this by separator.
|
||||||
|
return string.Format("{0}{1}{0}", SeparatorChar, string.Join(SeparatorChar.ToString(), _segments, 0, _segments.Length - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Removes the parent path.</summary>
|
||||||
|
/// <param name="parent">The parent.</param>
|
||||||
|
/// <returns>Path without first segments that are parent of path.</returns>
|
||||||
|
/// <exception cref="System.ArgumentException">
|
||||||
|
/// The specified path can not be the parent of this path: it is not a directory.
|
||||||
|
/// or The specified path is not a parent of this path.</exception>
|
||||||
|
internal Path RemoveParent(Path parent)
|
||||||
|
{
|
||||||
|
if (!parent.IsDirectory)
|
||||||
|
throw new ArgumentException("The specified path can not be the parent of this path: it is not a directory.");
|
||||||
|
if (!_path.StartsWith(_path))
|
||||||
|
throw new ArgumentException("The specified path is not a parent of this path.");
|
||||||
|
|
||||||
|
return new Path(_path.Remove(0, parent._path.Length - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Path AddParent(Path parent)
|
||||||
|
{
|
||||||
|
return parent.ToString().TrimEnd(SeparatorChar) + this;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Operators
|
||||||
|
|
||||||
|
/// <summary>Strings the specified path.</summary>
|
||||||
|
/// <param name="path">The path.</param>
|
||||||
|
public static implicit operator string(Path path)
|
||||||
|
{
|
||||||
|
return path._path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Convert string to path.</summary>
|
||||||
|
/// <param name="path">The path.</param>
|
||||||
|
public static implicit operator Path(string path)
|
||||||
|
{
|
||||||
|
return new Path(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Determines whether the specified <see cref="System.Object" /> is equal to this instance.</summary>
|
||||||
|
/// <param name="obj">The <see cref="System.Object" /> to compare with this instance.</param>
|
||||||
|
/// <returns>Return <c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>.</returns>
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
if (obj is Path)
|
||||||
|
return Equals((Path)obj);
|
||||||
|
else if (obj is String)
|
||||||
|
return _path.Equals(obj);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Determines whether the specified <see cref="Path" /> is equal to this instance.</summary>
|
||||||
|
/// <param name="other">The <see cref="Path" /> to compare with this instance.</param>
|
||||||
|
/// <returns>Return <c>true</c> if the specified <see cref="Path" /> is equal to this instance; otherwise, <c>false</c>.</returns>
|
||||||
|
public bool Equals(Path other)
|
||||||
|
{
|
||||||
|
if (other == null || other._path == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return other._path.Equals(_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a hash code for this instance.</summary>
|
||||||
|
/// <returns>A hash code for this instance, suitable for use in
|
||||||
|
/// hashing algorithms and data structures like a hash table. </returns>
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return _path.GetHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Equals operator.</summary>
|
||||||
|
/// <param name="p1">The first path.</param>
|
||||||
|
/// <param name="p2">The second path.</param>
|
||||||
|
/// <returns>Return <c>true</c> if values are equal, otherwise <c>false</c>.</returns>
|
||||||
|
public static bool operator ==(Path p1, Path p2)
|
||||||
|
{
|
||||||
|
if (System.Object.ReferenceEquals(p1, p2))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (((object)p1 == null) || ((object)p2 == null))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return p1.Equals(p2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Not equal operator.</summary>
|
||||||
|
/// <param name="p1">The first path.</param>
|
||||||
|
/// <param name="p2">The second path.</param>
|
||||||
|
/// <returns>Return <c>true</c> if values are not equal, otherwise <c>false</c>.</returns>
|
||||||
|
public static bool operator !=(Path p1, Path p2)
|
||||||
|
{
|
||||||
|
return !(p1 == p2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Append operator.</summary>
|
||||||
|
/// <param name="p1">The path.</param>
|
||||||
|
/// <param name="p2">The string to append.</param>
|
||||||
|
/// <returns>New instance path combined from parent and new name.</returns>
|
||||||
|
public static Path operator +(Path p1, string p2)
|
||||||
|
{
|
||||||
|
return p1.Append(p2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a <see cref="System.String" /> that represents this instance.</summary>
|
||||||
|
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return _path;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Operators
|
||||||
|
}
|
||||||
|
}
|
||||||
263
VirtualFS/Physical/PhysicalFileSystem.cs
Normal file
263
VirtualFS/Physical/PhysicalFileSystem.cs
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
/*
|
||||||
|
* 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.Text.RegularExpressions;
|
||||||
|
using VirtualFS.Base;
|
||||||
|
using VirtualFS.Implementation;
|
||||||
|
|
||||||
|
namespace VirtualFS.Physical
|
||||||
|
{
|
||||||
|
/// <summary>Physical file system implementation.</summary>
|
||||||
|
public class PhysicalFileSystem : BaseFileSystem
|
||||||
|
{
|
||||||
|
private string _root;
|
||||||
|
|
||||||
|
/// <summary>Initializes a new instance of the
|
||||||
|
/// <see cref="PhysicalFileSystem"/> class.</summary>
|
||||||
|
/// <param name="dir">The directory.</param>
|
||||||
|
public PhysicalFileSystem(System.IO.DirectoryInfo dir)
|
||||||
|
: base(false)
|
||||||
|
{
|
||||||
|
_root = dir.FullName;
|
||||||
|
|
||||||
|
if (!dir.Exists)
|
||||||
|
dir.Create();
|
||||||
|
|
||||||
|
if (_root.EndsWith(System.IO.Path.DirectorySeparatorChar.ToString()))
|
||||||
|
_root = _root.TrimEnd(System.IO.Path.DirectorySeparatorChar);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Entry FileSystemInfoToEntry(System.IO.FileSystemInfo fsi, string rootPath)
|
||||||
|
{
|
||||||
|
if (fsi is System.IO.DirectoryInfo)
|
||||||
|
return new Directory
|
||||||
|
{
|
||||||
|
IsReadOnly = IsReadOnly,
|
||||||
|
Path = rootPath + string.Format("{0}{1}", fsi.Name, Path.SeparatorChar),
|
||||||
|
FileSystem = this,
|
||||||
|
CreationTime = fsi.CreationTimeUtc,
|
||||||
|
LastAccessTime = fsi.LastAccessTime,
|
||||||
|
LastWriteTime = fsi.LastWriteTime,
|
||||||
|
};
|
||||||
|
else if (fsi is System.IO.FileInfo)
|
||||||
|
return new File
|
||||||
|
{
|
||||||
|
IsReadOnly = IsReadOnly && ((System.IO.FileInfo)fsi).IsReadOnly,
|
||||||
|
Path = rootPath + fsi.Name,
|
||||||
|
FileSystem = this,
|
||||||
|
CreationTime = fsi.CreationTimeUtc,
|
||||||
|
LastAccessTime = fsi.LastAccessTime,
|
||||||
|
LastWriteTime = fsi.LastWriteTime,
|
||||||
|
};
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
if (path.IsDirectory)
|
||||||
|
return System.IO.Directory.Exists(EntryRealPath(path));
|
||||||
|
else
|
||||||
|
return System.IO.File.Exists(EntryRealPath(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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;
|
||||||
|
System.IO.FileSystemInfo fsi = null;
|
||||||
|
|
||||||
|
if (path.IsDirectory)
|
||||||
|
fsi = new System.IO.DirectoryInfo(EntryRealPath(path));
|
||||||
|
else
|
||||||
|
fsi = new System.IO.FileInfo(EntryRealPath(path));
|
||||||
|
|
||||||
|
if (fsi.Exists)
|
||||||
|
result = FileSystemInfoToEntry(fsi, path);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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>
|
||||||
|
/// <exception cref="System.InvalidOperationException">Path is
|
||||||
|
/// not a directory, thus it has no entries.</exception>
|
||||||
|
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."));
|
||||||
|
|
||||||
|
return new System.IO.DirectoryInfo(EntryRealPath(path))
|
||||||
|
.EnumerateFileSystemInfos()
|
||||||
|
.Where(f => mask == null || mask.IsMatch(f.Name))
|
||||||
|
.Select(f => FileSystemInfoToEntry(f, path));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
return _root + ((string)path).Replace(Path.SeparatorChar, System.IO.Path.DirectorySeparatorChar);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Moves an existing file or directory to a new location.</summary>
|
||||||
|
/// <param name="source">Source path of file or directory.</param>
|
||||||
|
/// <param name="destination">Destination path. This path must ba a directory.</param>
|
||||||
|
/// <param name="overwrite">If <c>true</c> the destination files and directories
|
||||||
|
/// can be overwritten; otherwise, <c>false</c>. Files that aren't moved
|
||||||
|
/// will remain on file system.</param>
|
||||||
|
/// <param name="leaveStructure">If <c>true</c> the source directories will be left intact;
|
||||||
|
/// otherwise, <c>false</c>.</param>
|
||||||
|
/// <exception cref="System.InvalidOperationException">Source file doesn't exist.</exception>
|
||||||
|
/// <remarks>This method uses simple yet universal approach.
|
||||||
|
/// it's much slower than native one so implementing file system consider
|
||||||
|
/// better approach for internal move.</remarks>
|
||||||
|
public override void Move(Path source, Path destination, bool overwrite = false, bool leaveStructure = false)
|
||||||
|
{
|
||||||
|
if (source == destination)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (IsReadOnly)
|
||||||
|
throw new InvalidOperationException(string.Format("Can't move file(s) '{0}' to '{1}' on read only file system.", source, destination));
|
||||||
|
|
||||||
|
if (source.IsDirectory && destination.IsDirectory && !leaveStructure && !Exists(destination) && Exists(destination.Parent))
|
||||||
|
System.IO.Directory.Move(EntryRealPath(source), EntryRealPath(destination));
|
||||||
|
else if (!source.IsDirectory && destination.IsDirectory && Exists(destination.Parent))
|
||||||
|
{
|
||||||
|
if (!Exists(source))
|
||||||
|
throw new InvalidOperationException("Source file doesn't exist.");
|
||||||
|
|
||||||
|
System.IO.File.Move(EntryRealPath(source), EntryRealPath(destination) + source.Name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
base.Move(source, destination, overwrite, leaveStructure);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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 (path.IsDirectory)
|
||||||
|
System.IO.Directory.Move(EntryRealPath(path), EntryRealPath(path.Parent) + newName);
|
||||||
|
else
|
||||||
|
System.IO.File.Move(EntryRealPath(path), EntryRealPath(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);
|
||||||
|
|
||||||
|
return (Directory)FileSystemInfoToEntry(System.IO.Directory.CreateDirectory(EntryRealPath(path)), path.Parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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);
|
||||||
|
string realPath = EntryRealPath(path);
|
||||||
|
|
||||||
|
if (System.IO.Directory.Exists(realPath))
|
||||||
|
System.IO.Directory.Delete(realPath, recursive);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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);
|
||||||
|
|
||||||
|
return new VirtualStream(this, System.IO.File.OpenRead(EntryRealPath(path)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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 override Stream FileOpenWrite(Path path)
|
||||||
|
{
|
||||||
|
base.FileOpenWrite(path);
|
||||||
|
string realPath = EntryRealPath(path);
|
||||||
|
|
||||||
|
if (System.IO.File.Exists(realPath))
|
||||||
|
return new VirtualStream(this, new FileStream(realPath, FileMode.Open, FileAccess.Write, FileShare.Read, 4096, FileOptions.None));
|
||||||
|
|
||||||
|
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>
|
||||||
|
/// <returns>A <see cref="Stream"/> that provides
|
||||||
|
/// write access to the file specified in path.</returns>
|
||||||
|
public override Stream FileCreate(Path path)
|
||||||
|
{
|
||||||
|
base.FileCreate(path);
|
||||||
|
string realPath = EntryRealPath(path);
|
||||||
|
|
||||||
|
return new VirtualStream(this, new FileStream(realPath, FileMode.CreateNew, FileAccess.Write, FileShare.Read, 4096));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Deletes the specified file. An exception is 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.FileCreate(path);
|
||||||
|
string realPath = EntryRealPath(path);
|
||||||
|
|
||||||
|
if (System.IO.File.Exists(realPath))
|
||||||
|
System.IO.File.Delete(realPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
VirtualFS/VirtualFS.csproj
Normal file
29
VirtualFS/VirtualFS.csproj
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFrameworks>netstandard2.0;net472;net6.0;net7.0;net8.0</TargetFrameworks>
|
||||||
|
<Description>Virtual File System library.</Description>
|
||||||
|
<Copyright>Copyright © RUSSEK Software 2012-2024</Copyright>
|
||||||
|
<Company>RUSSEK Software</Company>
|
||||||
|
<Authors>Grzegorz Russek</Authors>
|
||||||
|
<VersionPrefix>1.6</VersionPrefix>
|
||||||
|
<RepositoryUrl>https://git.dr4cul4.pl/RUSSEK-Software/VirtualFS</RepositoryUrl>
|
||||||
|
<PackageProjectUrl>https://dr4cul4.pl</PackageProjectUrl>
|
||||||
|
<Product>VirtualFS</Product>
|
||||||
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<IncludeSymbols>true</IncludeSymbols>
|
||||||
|
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||||
|
<PackageReference Include="SharpZipLib" Version="1.4.2" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup Condition="$(TargetFramework.StartsWith('net4')) AND '$(MSBuildRuntimeType)' == 'Core' AND '$(OS)' != 'Windows_NT'">
|
||||||
|
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="All" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
Reference in New Issue
Block a user