xoofx / zio

A cross-platform abstract/virtual filesystem framework with many built-ins filesystems for .NET
BSD 2-Clause "Simplified" License
822 stars 61 forks source link

Add symlink support #85

Closed GerardSmit closed 4 months ago

GerardSmit commented 4 months ago

Fixes #74

This PR implements IFileSystem.CreateSymbolicLink(UPath path, UPath pathToTarget)

Linux

OS: Ubuntu 20.04 focal Kernel: x86_64 Linux 5.10.102.1-microsoft-standard-WSL2

$ dotnet test -f net8.0 --filter Symlink
Passed!  - Failed:     0, Passed:     2, Skipped:     0, Total:     2, Duration: 4 ms - Zio.Tests.dll (net8.0)

After adding .NET 6.0 to the targets (to validate the .NET Standard 2.1 implementation):

$ dotnet test -f net6.0 --filter Symlink
Passed!  - Failed:     0, Passed:     2, Skipped:     0, Total:     2, Duration: 3 ms - Zio.Tests.dll (net6.0)

Windows

OS: Windows 11 23H2 Note: needs to be tested with administrator rights

$ dotnet test -f net8.0 --filter Symlink
Passed!  - Failed:     0, Passed:     2, Skipped:     0, Total:     2, Duration: 19 ms - Zio.Tests.dll (net8.0)

$ dotnet test -f net472 --filter Symlink
Passed!  - Failed:     0, Passed:     2, Skipped:     0, Total:     2, Duration: 25 ms - Zio.Tests.dll (net472)
xoofx commented 4 months ago

Looks great, thanks!

I'm thinking that if we want to add this, we might want also to add File.ResolveLinkTarget as part of this PR?

The API would be similar, but taking a UPath and maybe returning a UPath? (Not ideal because we would lose the information of whether this is a file or directory...)

GerardSmit commented 4 months ago

I've added the API, can you validate if this is how you want the API?

interface IFileSystem
{
    /// <summary>
    /// Resolves the target of a symbolic link.
    /// </summary>
    /// <param name="linkPath">The path of the symbolic link to resolve.</param>
    UPath? ResolveLinkTarget(UPath linkPath);
}

I wasn't sure about the return type, since the following is possible:

  1. Nullable<UPath> / UPath? - the consumer knows that the result can be null (current implementation)
  2. UPath - the consumer has to check for UPath.IsEmpty / default.
  3. UPath with IOException if the path doesn't exists.
  4. Other, e.g. LinkResult? with: class LinkResult { UPath Path, bool IsDirectory }

Other than the return type: the only thing I'll have to validate if the MountFileSystem and SubFileSystem are returning the correct path.

xoofx commented 4 months ago

Maybe something like this instead?

interface IFileSystem
{
    /// <summary>
    /// Resolves the target of a symbolic link.
    /// </summary>
    /// <param name="linkPath">The path of the symbolic link to resolve.</param>
    /// <param name="resolvedPath">The path of the symbolic link resolved if true is returned.</param>
    bool TryResolveLinkTarget(UPath linkPath, out UPath resolvedPath);
}
xoofx commented 4 months ago

Thanks!