Skip to content

Instantly share code, notes, and snippets.

@Artfunkel
Last active August 29, 2015 14:04
Show Gist options
  • Save Artfunkel/c5a642b90a3e7a65eb7d to your computer and use it in GitHub Desktop.
Save Artfunkel/c5a642b90a3e7a65eb7d to your computer and use it in GitHub Desktop.
Python\Product\Analysis\SymbolicLinkTools.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Win32.SafeHandles;
// Code in this file courtesy of Troy Parsons
// http://troyparsons.com/blog/2012/03/symbolic-links-in-c-sharp/
namespace Microsoft.PythonTools.Analysis
{
/// <remarks>
/// Refer to http://msdn.microsoft.com/en-us/library/windows/hardware/ff552012%28v=vs.85%29.aspx
/// </remarks>
[StructLayout(LayoutKind.Sequential)]
public struct SymbolicLinkReparseData
{
// Not certain about this!
private const int maxUnicodePathLength = 260 * 2;
public uint ReparseTag;
public ushort ReparseDataLength;
public ushort Reserved;
public ushort SubstituteNameOffset;
public ushort SubstituteNameLength;
public ushort PrintNameOffset;
public ushort PrintNameLength;
public uint Flags;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = maxUnicodePathLength)]
public byte[] PathBuffer;
}
public static class SymbolicLink
{
private const uint genericReadAccess = 0x80000000;
private const uint fileFlagsForOpenReparsePointAndBackupSemantics = 0x02200000;
private const int ioctlCommandGetReparsePoint = 0x000900A8;
private const uint openExisting = 0x3;
private const uint pathNotAReparsePointError = 0x80071126;
private const uint shareModeAll = 0x7; // Read, Write, Delete
private const uint symLinkTag = 0xA000000C;
private const int targetIsAFile = 0;
private const int targetIsADirectory = 1;
[DllImport("kernel32.dll", SetLastError = true)]
private static extern SafeFileHandle CreateFile(
string lpFileName,
uint dwDesiredAccess,
uint dwShareMode,
IntPtr lpSecurityAttributes,
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
IntPtr hTemplateFile);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool CreateSymbolicLink(string lpSymlinkFileName, string lpTargetFileName, int dwFlags);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool DeviceIoControl(
IntPtr hDevice,
uint dwIoControlCode,
IntPtr lpInBuffer,
int nInBufferSize,
IntPtr lpOutBuffer,
int nOutBufferSize,
out int lpBytesReturned,
IntPtr lpOverlapped);
public static void CreateDirectoryLink(string linkPath, string targetPath)
{
if (!CreateSymbolicLink(linkPath, targetPath, targetIsADirectory) || Marshal.GetLastWin32Error() != 0)
{
try
{
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
}
catch (COMException exception)
{
throw new IOException(exception.Message, exception);
}
}
}
public static void CreateFileLink(string linkPath, string targetPath)
{
if (!CreateSymbolicLink(linkPath, targetPath, targetIsAFile))
{
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
}
}
public static bool Exists(string path)
{
if (!Directory.Exists(path) && !File.Exists(path))
{
return false;
}
string target = GetTarget(path);
return target != null;
}
private static SafeFileHandle getFileHandle(string path)
{
return CreateFile(path, genericReadAccess, shareModeAll, IntPtr.Zero, openExisting,
fileFlagsForOpenReparsePointAndBackupSemantics, IntPtr.Zero);
}
public static string GetTarget(string path)
{
if (String.IsNullOrEmpty(path))
return null;
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;
}
public static string ConvertToHardPath(string path)
{
if (String.IsNullOrEmpty(path)) return path;
var dir_info = new FileInfo(GetTarget(path) ?? path).Directory;
var final_path = new List<string>();
final_path.Add(Path.GetFileName(path));
while (dir_info != null)
{
var symlink_target = GetTarget(dir_info.FullName);
if (symlink_target != null)
dir_info = new DirectoryInfo(symlink_target);
final_path.Add(dir_info.Name);
dir_info = dir_info.Parent;
}
final_path.Reverse();
return Path.Combine(final_path.ToArray());
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment