Check if a file is real or a symbolic link
Asked Answered
S

9

47

Is there a way to tell using C# if a file is real or a symbolic link?

I've dug through the MSDN W32 docs, and can't find anything for checking this. I'm using CreateSymbolicLink from here, and it's working fine.

Strabismus answered 28/9, 2009 at 2:36 Comment(0)
S
25

I have some source code for symlinks posted on my blog that will allow you to:

  • create symlinks
  • check whether a path is a symlink
  • retrieve the target of a symlink

It also contains NUnit test cases, that you may wish to extend.

The meaty bit is:

private static SafeFileHandle getFileHandle(string path)
{
    return CreateFile(path, genericReadAccess, shareModeAll, IntPtr.Zero, openExisting,
        fileFlagsForOpenReparsePointAndBackupSemantics, IntPtr.Zero);
}

public static string GetTarget(string path)
{
    SymbolicLinkReparseData reparseDataBuffer;

    using (SafeFileHandle fileHandle = getFileHandle(path))
    {
        if (fileHandle.IsInvalid)
        {
            Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
        }

        int outBufferSize = Marshal.SizeOf(typeof(SymbolicLinkReparseData));
        IntPtr outBuffer = IntPtr.Zero;
        try
        {
            outBuffer = Marshal.AllocHGlobal(outBufferSize);
            int bytesReturned;
            bool success = DeviceIoControl(
                fileHandle.DangerousGetHandle(), ioctlCommandGetReparsePoint, IntPtr.Zero, 0,
                outBuffer, outBufferSize, out bytesReturned, IntPtr.Zero);

            fileHandle.Close();

            if (!success)
            {
                if (((uint)Marshal.GetHRForLastWin32Error()) == pathNotAReparsePointError)
                {
                    return null;
                }
                Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
            }

            reparseDataBuffer = (SymbolicLinkReparseData)Marshal.PtrToStructure(
                outBuffer, typeof(SymbolicLinkReparseData));
        }
        finally
        {
            Marshal.FreeHGlobal(outBuffer);
        }
    }
    if (reparseDataBuffer.ReparseTag != symLinkTag)
    {
        return null;
    }

    string target = Encoding.Unicode.GetString(reparseDataBuffer.PathBuffer,
        reparseDataBuffer.PrintNameOffset, reparseDataBuffer.PrintNameLength);

    return target;
}

That is:

Sajovich answered 27/3, 2012 at 3:2 Comment(5)
If it's possible, can you copy paste here complete code, need to go into blong, otherwise won't compile ?Fantinlatour
This code is now a NuGet package and is on GitHub.Cosmorama
This does not work for unicode file names; simple correction add CharSet = ChartSet.Auto to the DllImport attribute on the CreateFile extern definition in SymbolcLink class.Tombolo
You get the same information by calling GetFileAttributes, followed by FindFirstFile/FindNextFile. See Determining Whether a Directory Is a Mounted Folder for detailed instructions.Teratoid
The link to Troy's blog is now dead. I'm not sure if it can be replaced by the Aaron's link to GitHub or not?Rhett
Y
62
private bool IsSymbolic(string path)
{
    FileInfo pathInfo = new FileInfo(path);
    return pathInfo.Attributes.HasFlag(FileAttributes.ReparsePoint);
}
Yuji answered 20/10, 2014 at 20:4 Comment(4)
This should be the accepted answer. It's simple, concise, and directly answers the question.Leeland
Is there something I am missing about this solution vs the accepted solution because this seems a lot nicer.Stinky
Just because a file has a reparse point associated with it does NOT mean it's a symbolic link. A reparse point is just an arbitrary set of custom data associated with a file. You need to inspect the ID of the reparse point data to determine if it actually defines a symbolic link. This answer will give false positives anytime it encounters a real file with reparse points. See here: msdn.microsoft.com/en-us/library/windows/desktop/…Venter
Indeed. Read the last line: msdn.microsoft.com/en-us/library/aa363940.aspxInvariable
S
25

I have some source code for symlinks posted on my blog that will allow you to:

  • create symlinks
  • check whether a path is a symlink
  • retrieve the target of a symlink

It also contains NUnit test cases, that you may wish to extend.

The meaty bit is:

private static SafeFileHandle getFileHandle(string path)
{
    return CreateFile(path, genericReadAccess, shareModeAll, IntPtr.Zero, openExisting,
        fileFlagsForOpenReparsePointAndBackupSemantics, IntPtr.Zero);
}

public static string GetTarget(string path)
{
    SymbolicLinkReparseData reparseDataBuffer;

    using (SafeFileHandle fileHandle = getFileHandle(path))
    {
        if (fileHandle.IsInvalid)
        {
            Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
        }

        int outBufferSize = Marshal.SizeOf(typeof(SymbolicLinkReparseData));
        IntPtr outBuffer = IntPtr.Zero;
        try
        {
            outBuffer = Marshal.AllocHGlobal(outBufferSize);
            int bytesReturned;
            bool success = DeviceIoControl(
                fileHandle.DangerousGetHandle(), ioctlCommandGetReparsePoint, IntPtr.Zero, 0,
                outBuffer, outBufferSize, out bytesReturned, IntPtr.Zero);

            fileHandle.Close();

            if (!success)
            {
                if (((uint)Marshal.GetHRForLastWin32Error()) == pathNotAReparsePointError)
                {
                    return null;
                }
                Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
            }

            reparseDataBuffer = (SymbolicLinkReparseData)Marshal.PtrToStructure(
                outBuffer, typeof(SymbolicLinkReparseData));
        }
        finally
        {
            Marshal.FreeHGlobal(outBuffer);
        }
    }
    if (reparseDataBuffer.ReparseTag != symLinkTag)
    {
        return null;
    }

    string target = Encoding.Unicode.GetString(reparseDataBuffer.PathBuffer,
        reparseDataBuffer.PrintNameOffset, reparseDataBuffer.PrintNameLength);

    return target;
}

That is:

Sajovich answered 27/3, 2012 at 3:2 Comment(5)
If it's possible, can you copy paste here complete code, need to go into blong, otherwise won't compile ?Fantinlatour
This code is now a NuGet package and is on GitHub.Cosmorama
This does not work for unicode file names; simple correction add CharSet = ChartSet.Auto to the DllImport attribute on the CreateFile extern definition in SymbolcLink class.Tombolo
You get the same information by calling GetFileAttributes, followed by FindFirstFile/FindNextFile. See Determining Whether a Directory Is a Mounted Folder for detailed instructions.Teratoid
The link to Troy's blog is now dead. I'm not sure if it can be replaced by the Aaron's link to GitHub or not?Rhett
C
11

Starting with .NET 6 you can use: FileSystemInfo.LinkTarget Property

Property description:

Gets the target path of the link located in FullName, or null if this FileSystemInfo instance doesn't represent a link.

For example:

static bool IsSymbolicLink(string path)
{
    FileInfo file = new FileInfo(path);
    return file.LinkTarget != null;
}
Caulfield answered 11/10, 2022 at 8:1 Comment(1)
This is new as of .NET 6, and should be the preferred way now. Thanks!Sen
G
6

Here is an example of differentiating files and directories from links to files and links to directories.

Links to either files or directories maintain their own attributes (creation date, permissions) separate from their targets.

File links can be deleted (e.g. using "del") without affecting the target file.

Directory links can be removed (e.g. "rmdir") without affecting the target directory. Take care when using "rd /s". This WILL remove the directory link target.

The key FileAttributes flag to check in both FileInfo and DirectoryInfo is FileAttributes.ReparsePoint.

static void Main( string[] args ) {
FileInfo file_info = new FileInfo(args[0]);
DirectoryInfo directory_info = new DirectoryInfo(args[0]);

bool is_file = file_info.Exists;
bool is_directory = directory_info.Exists;

if (is_file) {
    Console.WriteLine(file_info.ToString() + " is a file");

    if ( file_info.Attributes.HasFlag(FileAttributes.ReparsePoint) )
        Console.WriteLine(args[0] + " is a Windows file link");
}
else if (is_directory) {
    Console.WriteLine(directory_info.ToString() + " is a directory");

    if ( directory_info.Attributes.HasFlag(FileAttributes.ReparsePoint) )
        Console.WriteLine(args[0] + " is a Windows directory link");
}
Gore answered 4/2, 2014 at 16:33 Comment(2)
Care to add a description? Code-only is sometimes okay but a few words to describe your answer can go a long way.Kilogrammeter
Again, relying on FileAttributes.ReparsePoint is not enough.Invariable
S
4

It proves the above answers are not reliable. Finally I got the right solution from MSDN:

To determine if a specified directory is a mounted folder, first call the GetFileAttributes function and inspect the FILE_ATTRIBUTE_REPARSE_POINT flag in the return value to see if the directory has an associated reparse point. If it does, use the FindFirstFile and FindNextFile functions to obtain the reparse tag in the dwReserved0 member of the WIN32_FIND_DATA structure. To determine if the reparse point is a mounted folder (and not some other form of reparse point), test whether the tag value equals the value IO_REPARSE_TAG_MOUNT_POINT. For more information, see Reparse Points.

Soneson answered 14/8, 2018 at 11:27 Comment(1)
Wow this is the first time I've ever come across a "reserved" field having a documented use without being renamed.Rhett
A
0

GetFileInformationByHandle fills a BY_HANDLE_FILE_INFORMATION structure which has a field dwFileAttributes where bits are set with info about the file's attributes (details here). In particular, look at the bit at mask...:

FILE_ATTRIBUTE_REPARSE_POINT 1024 0x0400

A file or directory that has an associated reparse point, or a file that is a symbolic link.

Acetify answered 28/9, 2009 at 2:42 Comment(6)
I've tried using the System.IO.File.GetAttributes() method, which I believe implements this, but it only seems to work on Junction Points, and not Symbolic Links.Strabismus
Can you try the syscall itself? I have no Vista install at hand to try this myself.Acetify
Same thing - getting 32, which is Archive only. I've just found out I may have to abort this method and use Hard Links anyway, but it would be good to figure it.Strabismus
Ah well - looks like MSDN is incorrect on this point, then:-(.Acetify
And I'm getting the same thing for Hard Links as well. Never shows it as a reparse point.Strabismus
Hrmm just found FSCTL_SET_REPARSE_POINT (msdn.microsoft.com/en-us/library/aa364595(VS.85).aspx). Maybe if it's created that way it will return 1024. Can't be bothered though - the date stamps and file sizes will be different in my scenario if it's not a link, so that will suffice.Strabismus
K
0

According to this answer to Stack Overflow question Find out whether a file is a symbolic link in PowerShell, getting the System.IO.FileAttributes for the file (via File.GetAttributes), and testing for the ReparsePoint bit, works. If the bit is set, it is a symlink or a junction point. If not, it is a regular file (or hardlink).

Kenley answered 12/2, 2010 at 22:31 Comment(1)
Again, relying on ReparsePoint is not enough.Deflect
C
0

The library MonoPosix provides API to check if a file is a symbolic link:

public bool IsSymlink(string filePath)
   => UnixFileSystemInfo.GetFileSystemEntry(filePath).IsSymbolicLink;
Churlish answered 28/1, 2020 at 8:5 Comment(0)
R
0

I know I am late to the party but found this discussion when researching same question

I found the below worked for me so thought I would post in case of use to anyone else

It works like this:-

var provider = ReparsePointFactory.Provider;

var link = provider.GetLink(@"c:\program files (x86)\common files\microsoft shared\vgx\vgx.dll");

MsgBox("Link Type: " + link.Type.ToString + " Link Target: " + link.Target + " Link Attributes: " + link.Attributes.ToString);

https://github.com/NCodeGroup/NCode.ReparsePoints https://www.nuget.org/packages/NCode.ReparsePoints/

Roundhouse answered 10/10, 2020 at 16:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.