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.
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.
I have some source code for symlinks posted on my blog that will allow you to:
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:
CreateFile()
DeviceIoControl()
to get the reparse point data (NOTE: it could be a junction point!)private bool IsSymbolic(string path)
{
FileInfo pathInfo = new FileInfo(path);
return pathInfo.Attributes.HasFlag(FileAttributes.ReparsePoint);
}
I have some source code for symlinks posted on my blog that will allow you to:
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:
CreateFile()
DeviceIoControl()
to get the reparse point data (NOTE: it could be a junction point!)Starting with .NET 6 you can use:
FileSystemInfo.LinkTarget
Property
Property description:
Gets the target path of the link located in
FullName
, ornull
if thisFileSystemInfo
instance doesn't represent a link.
For example:
static bool IsSymbolicLink(string path)
{
FileInfo file = new FileInfo(path);
return file.LinkTarget != null;
}
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");
}
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.
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 0x0400A file or directory that has an associated reparse point, or a file that is a symbolic link.
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).
The library MonoPosix provides API to check if a file is a symbolic link:
public bool IsSymlink(string filePath)
=> UnixFileSystemInfo.GetFileSystemEntry(filePath).IsSymbolicLink;
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/
© 2022 - 2024 — McMap. All rights reserved.