runtime: .NET Core is missing suitable APIs for dealing with filesystems that contain symbolic links

Ref: https://github.com/aspnet/Home/issues/2774 for discovery point

.NET Core is missing suitable APIs for dealing with filesystems that contain symbolic links

FileInfo does not resolve symbolic links, leading to subtle bugs. There’s no API that gives information about the link target. There’s other problems with the existing APIs causing them to have trouble when you call Directory.EnumerateFileSystemEntries involving having to do not one but two stat calls for each node. Might as well resolve them all at once.

Proposed API surface:

    // These are deliberately set to the *nix constant values where possible.
    public enum FileTypes {
          Missing = 0,
          Fifo = 0010000,
          CharacterDevice = 0020000,
          Directory = 0040000,
          BlockDevice = 0060000,
          File = 010000,
          SymbolicLink = 0120000,
          Socket = 0140000,
          SymbolicLinkMissingTarget = 0200000,
          SymbolicLinkLoop = 0400000,
          ReparsePoint = 01000000, // ReparsePoint attribute set but not a symbolic link
    }

    public struct FileNode {
        public string Path { get; private set; }
        public string FileName { get => System.IO.Path.GetFileName(Path); }
        public DateTime LastAccessTime { get; private set; }
        public DateTime LastAccessTimeUTC { get; private set; }
        public DateTime LastWriteTime { get; private set; }
        public DateTime LastWriteTimeUTC { get; private set; }
        public DateTime LastChangeTime { get; private set; }
        public DateTime LastChangeTimeUTC { get; private set; }
        public FileAttributes Attributes { get; private set; }
        public FileTypes FileNodeType { get; private set; }
        public string SymbolicLinkTargetPath { get; private set; }
        public bool Exists { get => FileNodeType != 0 }
        FileNode(string path, bool resolvesymboliclink)
        {
                /* The general idea of this API is it doesn't throw; just gives the appropriate information You could probably put Cer.Success on it. */
                Path = path;
                bool statpermissiondenied;
                if (resolvesymboliclink)
                {
                    /* This code path would call CreateFile with only FILE_READ_ATTRIBUTES and then call GetFileInformationByHandle; on AccessDenied or PermissionDenied fall through below */
                    /* on unix this would be a stat() call */
                }
                /* This code path would call FindFirstFileEx to get the file information by name from the node attribute */
                if (resolvesymboliclink && FileNodeType == FileTypes.SymbolicLink)
                   FileNodeType = 0;
        }
        /* Deserialization constructor */
        FileNode(string path, FileTypes fileNodeType, FileAttributes Attributes, DateTime lastAccessTimeUTC, DateTime lastChangeTimeUTC, DateTime lastWriteTimeUTC, string symbolicLinkTargetPath);
    }

    public partial class File {
         public static void CreateSymbolicLink(string path, string targetPath, bool targetisdirectory = false);
    }


    public partial class Directory {
         // This one exists only code readability
         // The idea is the programmer would normally only pass the third parameter if it was indirection from another layer of indirection, and otherwise would call File.CreateSymbolicLink to create a symbolic link to a file and Directory.CreateSymbolicLink to create a symbolic link to a directory
         public static void CreateSymbolicLink(string path, string targetPath, bool targetisdirectory = true);
                => File.CreateSymbolicLink(path, targetPath, targetisdirectory);
    }

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 6
  • Comments: 26 (13 by maintainers)

Commits related to this issue

Most upvoted comments

Hello, Is there any news on this ? at least a workaround to allow netcore application to follow symlinks ? It seems to be a basic feature on linux environment. Thanks

FileInfo, etc. are documented to work on the link itself. CreateFile doesn’t open the symlink itself, it opens the target. While I agree that can be confusing, it works in most cases. Your example isn’t safe even if they were aligned better. Even if you check Exists on the target there is a risk the file will be gone when you try to create the FileStream around it, let alone any number of other IO related errors (access issues, volume dismounts, etc.).

We could potentially add a TargetExists or something like that to FileInfo/DirectoryInfo. (Or maybe FileInfo Target { get; } that gives you back this or the link target if applicable.)

    var info = new FileInfo(path).Target;
    if (info.Exists && (info.Attributes & FileAttribtes.Directory) == 0 && info.Length > 0)
        try
        {
          using (var f = new FileStream(path, ...))
          {
             // Do something with f
          }
        }
        catch (Exception e)
        {
          // handle IO errors
        }

I absolutely agree that we need better symbolic link support, but I don’t feel depreciating existing APIs is the way to do it.

@xp-1000 : Here’s something to play with

https://github.com/joshudson/Emet/tree/master/FileSystems

I published Emet.FileSystems 0.0.1-alpha1 on nuget; it’s still verifying right now.

In PowerShell we already dynamically add a Target property to FileInfo/DirectoryInfo objects.