From 1f3f1c9b896d5ed898b6441112a14e3fd0adc60d Mon Sep 17 00:00:00 2001 From: Mike Nolan Date: Mon, 22 Jul 2024 18:44:49 -0500 Subject: [PATCH] Added Storage access framework, DiscUtils More of Zio, Fixed Dispose and internal function UnixPath.SetPath --- Tesses.VFS.sln | 12 + Tesses.VirtualFilesystem.Base/Class1.cs | 6 +- .../Tesses.VirtualFilesystem.Base.csproj | 6 +- Tesses.VirtualFilesystem.DiscUtils/Class1.cs | 103 ++++ .../Tesses.VirtualFilesystem.DiscUtils.csproj | 36 ++ Tesses.VirtualFilesystem.Local/Class1.cs | 5 + .../Tesses.VirtualFilesystem.Local.csproj | 6 +- Tesses.VirtualFilesystem.Saf/Class1.cs | 510 ++++++++++++++++++ Tesses.VirtualFilesystem.Saf/README.md | 99 ++++ .../Tesses.VirtualFilesystem.Saf.csproj | 32 ++ Tesses.VirtualFilesystem.Zio/Class1.cs | 303 ++++++++++- .../Tesses.VirtualFilesystem.Zio.csproj | 12 +- 12 files changed, 1117 insertions(+), 13 deletions(-) create mode 100644 Tesses.VirtualFilesystem.DiscUtils/Class1.cs create mode 100644 Tesses.VirtualFilesystem.DiscUtils/Tesses.VirtualFilesystem.DiscUtils.csproj create mode 100644 Tesses.VirtualFilesystem.Saf/Class1.cs create mode 100644 Tesses.VirtualFilesystem.Saf/README.md create mode 100644 Tesses.VirtualFilesystem.Saf/Tesses.VirtualFilesystem.Saf.csproj diff --git a/Tesses.VFS.sln b/Tesses.VFS.sln index f9d7c5f..62ed8b7 100644 --- a/Tesses.VFS.sln +++ b/Tesses.VFS.sln @@ -15,6 +15,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tesses.VirtualFilesystem.Lo EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tesses.VirtualFilesystem.Zio", "Tesses.VirtualFilesystem.Zio\Tesses.VirtualFilesystem.Zio.csproj", "{F30FD55D-194C-437E-AAD2-6E1E0FEEF1C2}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tesses.VirtualFilesystem.DiscUtils", "Tesses.VirtualFilesystem.DiscUtils\Tesses.VirtualFilesystem.DiscUtils.csproj", "{D399E2F3-B852-443E-8117-4B715EB84953}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tesses.VirtualFilesystem.Saf", "Tesses.VirtualFilesystem.Saf\Tesses.VirtualFilesystem.Saf.csproj", "{61FADB48-263C-494E-A564-A477B0D8C3F3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -48,5 +52,13 @@ Global {F30FD55D-194C-437E-AAD2-6E1E0FEEF1C2}.Debug|Any CPU.Build.0 = Debug|Any CPU {F30FD55D-194C-437E-AAD2-6E1E0FEEF1C2}.Release|Any CPU.ActiveCfg = Release|Any CPU {F30FD55D-194C-437E-AAD2-6E1E0FEEF1C2}.Release|Any CPU.Build.0 = Release|Any CPU + {D399E2F3-B852-443E-8117-4B715EB84953}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D399E2F3-B852-443E-8117-4B715EB84953}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D399E2F3-B852-443E-8117-4B715EB84953}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D399E2F3-B852-443E-8117-4B715EB84953}.Release|Any CPU.Build.0 = Release|Any CPU + {61FADB48-263C-494E-A564-A477B0D8C3F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {61FADB48-263C-494E-A564-A477B0D8C3F3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {61FADB48-263C-494E-A564-A477B0D8C3F3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {61FADB48-263C-494E-A564-A477B0D8C3F3}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/Tesses.VirtualFilesystem.Base/Class1.cs b/Tesses.VirtualFilesystem.Base/Class1.cs index 4a1c454..89efaec 100644 --- a/Tesses.VirtualFilesystem.Base/Class1.cs +++ b/Tesses.VirtualFilesystem.Base/Class1.cs @@ -488,7 +488,7 @@ namespace Tesses.VirtualFilesystem } - public interface IVirtualWatcher + public interface IVirtualWatcher : IDisposable { event EventHandler Changed; @@ -527,6 +527,7 @@ namespace Tesses.VirtualFilesystem /// in . /// bool IncludeSubdirectories { get; set; } + } public class VirtualWatcherRenamedEventArgs : VirtualWatcherChangedArgs @@ -871,7 +872,10 @@ namespace Tesses.VirtualFilesystem { pa.Append($"/{_parts[i]}"); } + if(pa.Length > 0) _path = pa.ToString(); + else + _path = "/"; } public UnixPath(UnixPath path1) { diff --git a/Tesses.VirtualFilesystem.Base/Tesses.VirtualFilesystem.Base.csproj b/Tesses.VirtualFilesystem.Base/Tesses.VirtualFilesystem.Base.csproj index ec3b398..1d21863 100644 --- a/Tesses.VirtualFilesystem.Base/Tesses.VirtualFilesystem.Base.csproj +++ b/Tesses.VirtualFilesystem.Base/Tesses.VirtualFilesystem.Base.csproj @@ -17,9 +17,9 @@ Tesses.VirtualFilesystem.Base Mike Nolan Tesses - 1.0.0 - 1.0.0 - 1.0.0 + 1.0.1 + 1.0.1 + 1.0.1 Another VirtualFilesystem for .NET GPL-3.0-only true diff --git a/Tesses.VirtualFilesystem.DiscUtils/Class1.cs b/Tesses.VirtualFilesystem.DiscUtils/Class1.cs new file mode 100644 index 0000000..8c58a85 --- /dev/null +++ b/Tesses.VirtualFilesystem.DiscUtils/Class1.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.IO; +using DiscUtils; +using Tesses.VirtualFilesystem; +using Tesses.VirtualFilesystem.Filesystems; + +namespace Tesses.VirtualFilesystem.Filesystems +{ + public class DiscUtilsFilesystem : SyncFileSystem + { + IFileSystem fs; + public DiscUtilsFilesystem(IFileSystem fs) + { + this.fs = fs; + } + public override string ConvertPathFromUnixPath(UnixPath path) + { + return path.Path.Replace("/","\\"); + } + public override UnixPath ConvertPathToUnixPath(string path) + { + return new UnixPath(path.Replace("\\","/")); + } + public override void CreateDirectory(UnixPath directory) + { + fs.CreateDirectory(ConvertPathFromUnixPath(directory)); + } + + public override void DeleteDirectory(UnixPath path) + { + fs.DeleteDirectory(ConvertPathFromUnixPath(path)); + } + + public override void DeleteFile(UnixPath path) + { + fs.DeleteFile(ConvertPathFromUnixPath(path)); + } + + public override bool DirectoryExists(UnixPath path) + { + return fs.DirectoryExists(ConvertPathFromUnixPath(path)); + } + + public override IEnumerable EnumerateFileSystemEntries(UnixPath path) + { + foreach(var item in fs.GetFileSystemEntries(ConvertPathFromUnixPath(path))) + { + yield return ConvertPathToUnixPath(item); + } + } + + public override bool FileExists(UnixPath path) + { + return fs.FileExists(ConvertPathFromUnixPath(path)); + } + + public override DateTime GetCreationTime(UnixPath path) + { + return fs.GetCreationTime(ConvertPathFromUnixPath(path)); + } + + public override DateTime GetLastAccessTime(UnixPath path) + { + return fs.GetLastAccessTime(ConvertPathFromUnixPath(path)); + } + + public override DateTime GetLastWriteTime(UnixPath path) + { + return fs.GetLastWriteTime(ConvertPathFromUnixPath(path)); + } + + public override void MoveDirectory(UnixPath src, UnixPath dest) + { + fs.MoveDirectory(ConvertPathFromUnixPath(src),ConvertPathFromUnixPath(dest)); + } + + public override void MoveFile(UnixPath src, UnixPath dest) + { + fs.MoveFile(ConvertPathFromUnixPath(src),ConvertPathFromUnixPath(dest)); + } + + public override Stream Open(UnixPath path, FileMode mode, FileAccess access, FileShare share) + { + return fs.OpenFile(ConvertPathFromUnixPath(path),mode,access); + } + + public override void SetCreationTime(UnixPath path, DateTime time) + { + fs.SetCreationTime(ConvertPathFromUnixPath(path),time); + } + + public override void SetLastAccessTime(UnixPath path, DateTime time) + { + fs.SetLastAccessTime(ConvertPathFromUnixPath(path),time); + } + + public override void SetLastWriteTime(UnixPath path, DateTime time) + { + fs.SetLastWriteTime(ConvertPathFromUnixPath(path),time); + } + } +} diff --git a/Tesses.VirtualFilesystem.DiscUtils/Tesses.VirtualFilesystem.DiscUtils.csproj b/Tesses.VirtualFilesystem.DiscUtils/Tesses.VirtualFilesystem.DiscUtils.csproj new file mode 100644 index 0000000..4cfd5a4 --- /dev/null +++ b/Tesses.VirtualFilesystem.DiscUtils/Tesses.VirtualFilesystem.DiscUtils.csproj @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + netstandard2.0 + true + Tesses.VirtualFilesystem.DiscUtils + Mike Nolan + Tesses + 1.0.0 + 1.0.0 + 1.0.0 + Another VirtualFilesystem for .NET + GPL-3.0-only + true + VFS, Filesystem, VirtualFilesystem + https://gitlab.tesses.net/tesses50/tesses-vfs + 8.0 + README.md + + + + diff --git a/Tesses.VirtualFilesystem.Local/Class1.cs b/Tesses.VirtualFilesystem.Local/Class1.cs index d62d151..f2474bb 100644 --- a/Tesses.VirtualFilesystem.Local/Class1.cs +++ b/Tesses.VirtualFilesystem.Local/Class1.cs @@ -165,6 +165,11 @@ namespace Tesses.VirtualFilesystem.Filesystems public event EventHandler Deleted; public event EventHandler Renamed; public event EventHandler Error; + + public void Dispose() + { + this.watcher.Dispose(); + } } public override IVirtualWatcher WatchDirectory(UnixPath dir) diff --git a/Tesses.VirtualFilesystem.Local/Tesses.VirtualFilesystem.Local.csproj b/Tesses.VirtualFilesystem.Local/Tesses.VirtualFilesystem.Local.csproj index c0e0997..094b6a0 100644 --- a/Tesses.VirtualFilesystem.Local/Tesses.VirtualFilesystem.Local.csproj +++ b/Tesses.VirtualFilesystem.Local/Tesses.VirtualFilesystem.Local.csproj @@ -17,9 +17,9 @@ Tesses.VirtualFilesystem.Local Mike Nolan Tesses - 1.0.0 - 1.0.0 - 1.0.0 + 1.0.1 + 1.0.1 + 1.0.1 Another VirtualFilesystem for .NET GPL-3.0-only true diff --git a/Tesses.VirtualFilesystem.Saf/Class1.cs b/Tesses.VirtualFilesystem.Saf/Class1.cs new file mode 100644 index 0000000..7bed79d --- /dev/null +++ b/Tesses.VirtualFilesystem.Saf/Class1.cs @@ -0,0 +1,510 @@ +using Android.Content; +using Tesses.VirtualFilesystem; +using Android.App; +using Android.Content.PM; +using AndroidX.DocumentFile.Provider; +using Tesses.VirtualFilesystem.Extensions; +namespace Tesses.VirtualFilesystem.Filesystems; + + +public class SAFFileSystem : SyncFileSystem +{ + public const int RequestCode = 1447775022; + public const string SharedPreferencesFile = "tesess_vfs"; + public static void RequestDirectory(Activity app,bool canwrite=true,bool presistant=false,int reqCode=RequestCode) + { + Intent intent = new Intent(Intent.ActionOpenDocumentTree); + intent.AddFlags(ActivityFlags.GrantReadUriPermission|(canwrite? ActivityFlags.GrantWriteUriPermission : 0)|(presistant?ActivityFlags.GrantPersistableUriPermission:0)); + app.StartActivityForResult(intent,reqCode); + } + + public static SAFFileSystem? GetSAFFromResponse(Context? app,Intent? intent) + { + if(app == null || intent == null) return null; + var uri=intent.Data; + if(uri != null) + { + return new SAFFileSystem(uri,app); + } + return null; + } + public static SAFFileSystem? GetSAFFromResponsePresistant(Context? app,Intent? intent,string key) + { + if(app == null || intent == null) return null; + var uri=intent.Data; + if(uri != null) + { + var r = app?.ContentResolver; + if(r == null) return null; + var rw = intent.Flags & (ActivityFlags.GrantReadUriPermission | ActivityFlags.GrantWriteUriPermission); + r.TakePersistableUriPermission(uri,rw); + var app2=app?.CreatePackageContext(app.PackageName,0); + var sharedPrefs=app2?.GetSharedPreferences(SharedPreferencesFile,FileCreationMode.Private); + + sharedPrefs?.Edit()?.PutString(key,uri.ToString())?.Apply(); + + + return new SAFFileSystem(uri,app); + } + return null; + } + public static void Revoke(Context? app,string key) + { + var app2=app?.CreatePackageContext(app.PackageName,0); + var sharedPrefs=app2?.GetSharedPreferences(SharedPreferencesFile,FileCreationMode.Private); + var res=sharedPrefs?.GetString(key,null); + var r = app?.ContentResolver; + if(r == null) return; + + if(string.IsNullOrWhiteSpace(res)) return; + var uri = Android.Net.Uri.Parse(res); + if(uri == null) return; + r.ReleasePersistableUriPermission(uri,ActivityFlags.GrantReadUriPermission); + sharedPrefs?.Edit()?.Remove(key)?.Apply(); + } + + public static SAFFileSystem? GetSAFFromSharedStorage(Context? app,string key) + { + if(app == null) return null; + var app2=app?.CreatePackageContext(app.PackageName,0); + var sharedPrefs=app2?.GetSharedPreferences(SharedPreferencesFile,FileCreationMode.Private); + var res=sharedPrefs?.GetString(key,null); + + if(string.IsNullOrWhiteSpace(res)) return null; + var uri = Android.Net.Uri.Parse(res); + if(uri == null) return null; + + return new SAFFileSystem(uri,app); + } + + + public SAFFileSystem(global::Android.Net.Uri uri,Context? ctx) + { + Uri = uri; + Context = ctx; + + } + public global::Android.Net.Uri Uri {get;set;} + public Context? Context {get;set;} + + + public override void CreateDirectory(UnixPath directory) + { + if(Context != null && Uri != null) + { + + var dir = DocumentFile.FromTreeUri(Context,Uri); + if(dir == null) return; + + foreach(var item in directory.Parts) + { + var dir2= dir?.FindFile(item); + if(dir2 != null) + { + if(!dir2.IsDirectory && !dir2.IsFile && !dir2.IsVirtual) + { + dir = dir2.CreateDirectory(item); + } + else if(dir2.IsDirectory) + { + dir = dir2; + } + } + } + + } + } + + public override void DeleteDirectory(UnixPath path) + { + if(Context != null && Uri != null) + { + + var dir = DocumentFile.FromTreeUri(Context,Uri); + if(dir == null) return; + + for(int i = 0;i EnumerateFileSystemEntries(UnixPath path) + { + if(Context != null && Uri != null) + { + + var dir = DocumentFile.FromTreeUri(Context,Uri); + if(dir == null) yield break; + + if(path.IsRoot) + { + foreach(var _item in dir.ListFiles()) + { + yield return path / _item.Name; + } + yield break; + } + + for(int i = 0;i "a" + //FileMode.Create -> "w" + //FileMode.CreateNew -> "w" + //FileMode.Open -> "r or w" + //FileMode.OpenOrCreate -> "r or w" + //FileMode.Truncate -> "t" + //"r", "w", "wt", "wa", "rw" or "rwt" + + Stream? strm = null; + + if(access == FileAccess.Read) + strm = Context?.ContentResolver?.OpenInputStream(uri) ?? null; + + if(access == FileAccess.Write) + if(mode == FileMode.Truncate) + strm = Context?.ContentResolver?.OpenOutputStream(uri,"wt"); + else if(mode == FileMode.Append) + strm = Context?.ContentResolver?.OpenOutputStream(uri,"wa"); + else + strm = Context?.ContentResolver?.OpenOutputStream(uri,"w"); + + if(access == FileAccess.ReadWrite) + if(mode == FileMode.Truncate) + strm = Context?.ContentResolver?.OpenOutputStream(uri,"rwt"); + else + strm = Context?.ContentResolver?.OpenOutputStream(uri,"rw"); + + + + + + if(strm == null) throw new IOException("Failed to open stream"); + return strm; + + } + + public override void SetCreationTime(UnixPath path, DateTime time) + { + + } + + public override void SetLastAccessTime(UnixPath path, DateTime time) + { + + } + + public override void SetLastWriteTime(UnixPath path, DateTime time) + { + + } +} diff --git a/Tesses.VirtualFilesystem.Saf/README.md b/Tesses.VirtualFilesystem.Saf/README.md new file mode 100644 index 0000000..406af04 --- /dev/null +++ b/Tesses.VirtualFilesystem.Saf/README.md @@ -0,0 +1,99 @@ +# Tesses.VirtualFilesystem.Saf + +A library to use Tesses.VirtualFilesystem with Storage Access Framework (Android) + +# Nuget + +# License + +![GPL3-only](https://www.gnu.org/graphics/gplv3-with-text-136x68.png) +Licensed under [GPL3](https://www.gnu.org/licenses/) + +# How to use (not presistant) +```csharp + ... + using Tesses.VirtualFilesystem; + using Tesses.VirtualFilesystem.Filesystems; + using Android.Content; + using AndroidX.DocumentFile.Provider; + using Tesses.VirtualFilesystem.Extensions; + ... + + void SomeActivityFunction() + { + SAFFileSystem.RequestDirectory(this,true); + } + protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent? data) + { + if(requestCode == SAFFileSystem.RequestCode && resultCode == Result.Ok) + { + var res=SAFFileSystem.GetSAFFromResponse(this,data); + if(res != null) + HandleFilesystem(res); + } + } + void HandleFilesystem(SAFFileSystem fs) + { + fs.CreateDirectory(Special.Root/"My Special Folder"); + //read https://gitea.site.tesses.net/tesses50/tesses-vfs to get up to speed + //root is relative to the folder the user chose + //so get subdir filesystem is not needed + } +``` + +# How to use (presistant) +```csharp + ... + using Tesses.VirtualFilesystem; + using Tesses.VirtualFilesystem.Filesystems; + using Android.Content; + using AndroidX.DocumentFile.Provider; + using Tesses.VirtualFilesystem.Extensions; + ... + const string MySAFKey = "MySafKey"; + void SomethingWithContext() + { + var res= SAFFileSystem.GetSAFFromSharedStorage(this,MySAFKey); + if(res != null) + HandleFilesystem(res); + } + void SomeActivityFunction() + { + var res= SAFFileSystem.GetSAFFromSharedStorage(this,MySAFKey); + if(res != null) + HandleFilesystem(res); + else + SAFFileSystem.RequestDirectory(this,true,true); + } + protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent? data) + { + if(requestCode == SAFFileSystem.RequestCode && resultCode == Result.Ok) + { + var res=SAFFileSystem.GetSAFFromResponsePresistant(this,data,MySAFKey); + if(res != null) + HandleFilesystem(res); + } + } + void HandleFilesystem(SAFFileSystem fs) + { + fs.CreateDirectory(Special.Root/"My Special Folder"); + //read https://gitea.site.tesses.net/tesses50/tesses-vfs to get up to speed + //root is relative to the folder the user chose + //so get subdir filesystem is not needed + } +``` +# How to revoke permissions +```csharp + ... + using Tesses.VirtualFilesystem; + using Tesses.VirtualFilesystem.Filesystems; + using Android.Content; + using AndroidX.DocumentFile.Provider; + using Tesses.VirtualFilesystem.Extensions; + ... + const string MySAFKey = "MySafKey"; + void SomethingWithContext() + { + SAFFileSystem.Revoke(this,MySAFKey); + } +``` diff --git a/Tesses.VirtualFilesystem.Saf/Tesses.VirtualFilesystem.Saf.csproj b/Tesses.VirtualFilesystem.Saf/Tesses.VirtualFilesystem.Saf.csproj new file mode 100644 index 0000000..4513a24 --- /dev/null +++ b/Tesses.VirtualFilesystem.Saf/Tesses.VirtualFilesystem.Saf.csproj @@ -0,0 +1,32 @@ + + + net8.0-android + 21 + enable + enable + true + Tesses.VirtualFilesystem.Saf + Mike Nolan + Tesses + 1.0.0 + 1.0.0 + 1.0.0 + Tesses.VirtualFilesystem for Android Storage Access Framework (Is slow though) + GPL-3.0-only + true + VFS, Filesystem, VirtualFilesystem + https://gitlab.tesses.net/tesses50/tesses-vfs + + README.md + + + + + + + + + + + + diff --git a/Tesses.VirtualFilesystem.Zio/Class1.cs b/Tesses.VirtualFilesystem.Zio/Class1.cs index bc33e33..0e027ff 100644 --- a/Tesses.VirtualFilesystem.Zio/Class1.cs +++ b/Tesses.VirtualFilesystem.Zio/Class1.cs @@ -21,13 +21,20 @@ using Tesses.VirtualFilesystem; using System.Collections.Generic; using System; using System.IO; +using Tesses.VirtualFilesystem.Extensions; namespace Tesses.VirtualFilesystem.Filesystems { - public class ZioFileSystem : SyncFileSystem { IFileSystem fs; + + public IFileSystem Filesystem => fs; + + public override void Dispose() + { + fs.Dispose(); + } public ZioFileSystem(IFileSystem fs) { this.fs = fs; @@ -171,7 +178,301 @@ namespace Tesses.VirtualFilesystem.Filesystems public event EventHandler Deleted; public event EventHandler Renamed; public event EventHandler Error; + + public void Dispose() + { + this.watcher.Dispose(); + } } } + + public class ZioMountableWrapper : ZioFileSystem + { + public MountFileSystem MountFilesystem {get;} + private ZioMountableWrapper(MountFileSystem fs) : base(fs) + { + MountFilesystem = fs; + } + + public static ZioMountableWrapper Create(IVirtualFilesystem fs,bool owned=true) + { + return new ZioMountableWrapper(new MountFileSystem(new TessesVFSFilesystem(fs),owned)); + } + public static ZioMountableWrapper Create(bool owned=true) + { + return new ZioMountableWrapper(new MountFileSystem(owned)); + } + public void Mount(UnixPath path,IVirtualFilesystem fs) + { + MountFilesystem.Mount(path.Path,new TessesVFSFilesystem(fs)); + } + public bool IsMounted(UnixPath path) + { + return MountFilesystem.IsMounted(path.Path); + } + public Dictionary GetMounts() + { + Dictionary fs=new Dictionary(); + + foreach(var item in MountFilesystem.GetMounts()) + { + var _fs=item.Value as TessesVFSFilesystem; + if(_fs !=null) + fs.Add(item.Key.FullName,_fs.Filesystem); + } + return fs; + + + } + + } + + } + +namespace Zio.FileSystems +{ + public class TessesVFSFilesystem : Zio.FileSystems.FileSystem + { + protected override void Dispose(bool disposing) + { + if(disposing) + fs.Dispose(); + } + IVirtualFilesystem fs; + public IVirtualFilesystem Filesystem => fs; + public TessesVFSFilesystem(IVirtualFilesystem fs) + { + this.fs = fs; + } + protected override UPath ConvertPathFromInternalImpl(string innerPath) + { + return innerPath; + } + + protected override string ConvertPathToInternalImpl(UPath path) + { + return path.FullName; + } + + protected override void CopyFileImpl(UPath srcPath, UPath destPath, bool overwrite) + { + if(!overwrite && fs.FileExists(fs.ConvertPathToUnixPath(ConvertPathToInternal(destPath)))) throw new IOException("File already exists"); + + fs.CopyFile(fs.ConvertPathToUnixPath(ConvertPathToInternal(srcPath)),fs.ConvertPathToUnixPath(ConvertPathToInternal(destPath))); + } + + protected override void CreateDirectoryImpl(UPath path) + { + fs.CreateDirectory(fs.ConvertPathToUnixPath(ConvertPathToInternal(path))); + } + + protected override void DeleteDirectoryImpl(UPath path, bool isRecursive) + { + fs.DeleteDirectory(fs.ConvertPathToUnixPath(ConvertPathToInternal(path)),isRecursive); + } + + protected override void DeleteFileImpl(UPath path) + { + fs.DeleteFile(fs.ConvertPathToUnixPath(ConvertPathToInternal(path))); + } + + protected override bool DirectoryExistsImpl(UPath path) + { + return fs.DirectoryExists(fs.ConvertPathToUnixPath(ConvertPathToInternal(path))); + } + + protected override IEnumerable EnumerateItemsImpl(UPath path, SearchOption searchOption, SearchPredicate searchPredicate) + { + foreach(var item in fs.EnumerateFileSystemEntries(fs.ConvertPathToUnixPath(ConvertPathToInternal(path)))) + { + yield return new FileSystemItem(this,ConvertPathFromInternal(fs.ConvertPathFromUnixPath(item)),fs.DirectoryExists(item)); + } + } + + protected override IEnumerable EnumeratePathsImpl(UPath path, string searchPattern, SearchOption searchOption, SearchTarget searchTarget) + { + foreach(var item in fs.EnumerateFileSystemEntries(fs.ConvertPathToUnixPath(ConvertPathToInternal(path)))) + { + yield return ConvertPathFromInternal(fs.ConvertPathFromUnixPath(item)); + } + } + + protected override bool FileExistsImpl(UPath path) + { + return fs.FileExists(fs.ConvertPathToUnixPath(ConvertPathToInternal(path))); + } + + protected override FileAttributes GetAttributesImpl(UPath path) + { + return fs.GetAttributes(fs.ConvertPathToUnixPath(ConvertPathToInternal(path))); + } + + protected override DateTime GetCreationTimeImpl(UPath path) + { + return fs.GetCreationTime(fs.ConvertPathToUnixPath(ConvertPathToInternal(path))); + } + + protected override long GetFileLengthImpl(UPath path) + { + using(var f = fs.OpenRead(fs.ConvertPathToUnixPath(ConvertPathToInternal(path)))) + { + return f.Length; + } + } + + protected override DateTime GetLastAccessTimeImpl(UPath path) + { + return fs.GetLastAccessTime(fs.ConvertPathToUnixPath(ConvertPathToInternal(path))); + } + + protected override DateTime GetLastWriteTimeImpl(UPath path) + { + return fs.GetLastWriteTime(fs.ConvertPathToUnixPath(ConvertPathToInternal(path))); + } + + protected override void MoveDirectoryImpl(UPath srcPath, UPath destPath) + { + fs.MoveDirectory(fs.ConvertPathToUnixPath(ConvertPathToInternal(srcPath)),fs.ConvertPathToUnixPath(ConvertPathToInternal(destPath))); + } + + protected override void MoveFileImpl(UPath srcPath, UPath destPath) + { + fs.MoveDirectory(fs.ConvertPathToUnixPath(ConvertPathToInternal(srcPath)),fs.ConvertPathToUnixPath(ConvertPathToInternal(destPath))); + + } + + protected override Stream OpenFileImpl(UPath path, FileMode mode, FileAccess access, FileShare share) + { + return fs.Open(fs.ConvertPathToUnixPath(ConvertPathToInternal(path)),mode,access,share); + } + + protected override void ReplaceFileImpl(UPath srcPath, UPath destPath, UPath destBackupPath, bool ignoreMetadataErrors) + { + MoveFile(destPath,destBackupPath); + MoveFile(srcPath,destPath); + } + + protected override void SetAttributesImpl(UPath path, FileAttributes attributes) + { + fs.SetAttributes(fs.ConvertPathToUnixPath(ConvertPathToInternal(path)),attributes); + } + + protected override void SetCreationTimeImpl(UPath path, DateTime time) + { + fs.SetCreationTime(fs.ConvertPathToUnixPath(ConvertPathToInternal(path)),time); + } + + protected override void SetLastAccessTimeImpl(UPath path, DateTime time) + { + fs.SetLastAccessTime(fs.ConvertPathToUnixPath(ConvertPathToInternal(path)),time); + } + + protected override void SetLastWriteTimeImpl(UPath path, DateTime time) + { + fs.SetLastWriteTime(fs.ConvertPathToUnixPath(ConvertPathToInternal(path)),time); + } + + protected override IFileSystemWatcher WatchImpl(UPath path) + { + return new _FSW(this,fs,fs.ConvertPathToUnixPath(ConvertPathToInternal(path))); + } + + protected override void CreateSymbolicLinkImpl(UPath path, UPath pathToTarget) + { + fs.CreateSymlink(fs.ConvertPathToUnixPath(ConvertPathToInternal(path)),fs.ConvertPathToUnixPath(ConvertPathToInternal(pathToTarget))); + } + + protected override bool TryResolveLinkTargetImpl(UPath linkPath, out UPath resolvedPath) + { + if(fs.SymlinkExists(fs.ConvertPathToUnixPath(ConvertPathToInternal(linkPath)))) + { + resolvedPath = ConvertPathFromInternal(fs.ConvertPathFromUnixPath(fs.ReadLink(fs.ConvertPathToUnixPath(ConvertPathToInternal(linkPath))))); + return true; + } + else + { + resolvedPath = ConvertPathFromInternal(fs.ConvertPathFromUnixPath(Special.Root)); + return false; + } + } + + private class _FSW : IFileSystemWatcher + { + IVirtualWatcher watcher; + + + + public _FSW(IFileSystem fs2,IVirtualFilesystem fs,UnixPath unixPath) + { + this.FileSystem=fs2; + watcher=fs.WatchDirectory(unixPath); + Path = fs2.ConvertPathFromInternal(fs.ConvertPathFromUnixPath(unixPath)); + this._fs = fs; + watcher.Changed += W_Changed; + watcher.Created += W_Created; + watcher.Deleted += W_Deleted; + watcher.Error += W_Error; + watcher.Renamed += W_Renamed; + } + + private void W_Renamed(object sender, VirtualWatcherRenamedEventArgs e) + { + Renamed?.Invoke(this,new FileRenamedEventArgs(FileSystem,(Zio.WatcherChangeTypes)e.ChangeType,FileSystem.ConvertPathFromInternal(_fs.ConvertPathFromUnixPath(e.FullPath)),FileSystem.ConvertPathFromInternal(_fs.ConvertPathFromUnixPath(e.OldFullPath)))); + } + + private void W_Error(object sender, System.IO.ErrorEventArgs e) + { + Error?.Invoke(this,new FileSystemErrorEventArgs(e.GetException())); + } + + private void W_Deleted(object sender, VirtualWatcherChangedArgs e) + { + Deleted?.Invoke(this,new FileChangedEventArgs(FileSystem,(Zio.WatcherChangeTypes)e.ChangeType,FileSystem.ConvertPathFromInternal(_fs.ConvertPathFromUnixPath(e.FullPath)))); + + } + + IVirtualFilesystem _fs; + private void W_Created(object sender, VirtualWatcherChangedArgs e) + { + Created?.Invoke(this,new FileChangedEventArgs(FileSystem,(Zio.WatcherChangeTypes)e.ChangeType,FileSystem.ConvertPathFromInternal(_fs.ConvertPathFromUnixPath(e.FullPath)))); + } + + private void W_Changed(object sender, VirtualWatcherChangedArgs e) + { + Changed?.Invoke(this,new FileChangedEventArgs(FileSystem,(Zio.WatcherChangeTypes)e.ChangeType,FileSystem.ConvertPathFromInternal(_fs.ConvertPathFromUnixPath(e.FullPath)))); + } + + public IFileSystem FileSystem {get;} + + public UPath Path {get;} + + public int InternalBufferSize { get => watcher.InternalBufferSize; set => watcher.InternalBufferSize = value; } + public Zio.NotifyFilters NotifyFilter { get => (Zio.NotifyFilters)watcher.NotifyFilter; set => watcher.NotifyFilter = (System.IO.NotifyFilters)value; } + public bool EnableRaisingEvents { get => watcher.EnableRaisingEvents; set => watcher.EnableRaisingEvents=value; } + public string Filter { get => watcher.Filter; set => watcher.Filter=value; } + public bool IncludeSubdirectories { get => watcher.IncludeSubdirectories; set => watcher.IncludeSubdirectories=value; } + + public event EventHandler Changed; + public event EventHandler Created; + public event EventHandler Deleted; + public event EventHandler Error; + public event EventHandler Renamed; + + + + public void Dispose() + { + watcher.Changed -= W_Changed; + watcher.Created -= W_Created; + watcher.Deleted -= W_Deleted; + watcher.Error -= W_Error; + watcher.Renamed -= W_Renamed; + watcher.Dispose(); + } + } + } + + +} \ No newline at end of file diff --git a/Tesses.VirtualFilesystem.Zio/Tesses.VirtualFilesystem.Zio.csproj b/Tesses.VirtualFilesystem.Zio/Tesses.VirtualFilesystem.Zio.csproj index ea22846..59e3680 100644 --- a/Tesses.VirtualFilesystem.Zio/Tesses.VirtualFilesystem.Zio.csproj +++ b/Tesses.VirtualFilesystem.Zio/Tesses.VirtualFilesystem.Zio.csproj @@ -2,14 +2,16 @@ + + - + - + @@ -18,9 +20,9 @@ Tesses.VirtualFilesystem.Zio Mike Nolan Tesses - 1.0.0 - 1.0.0 - 1.0.0 + 1.0.1 + 1.0.1 + 1.0.1 Another VirtualFilesystem for .NET GPL-3.0-only true