Last active

Embed URL

HTTPS clone URL

SSH clone URL

You can clone with HTTPS or SSH.

Download Gist

Python\Product\Analysis\SymbolicLinkTools.cs

View SymbolicLinkTools.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
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
Something went wrong with that request. Please try again.