private NativeMethods.ReparseDataBuffer GetReparsePointInfo() { const uint fileOpenReparsePoint = 0x00200000; const uint fileFlagBackupSemantics = 0x02000000; const FileOptions flags = (FileOptions)(fileOpenReparsePoint | fileFlagBackupSemantics); using (var handle = NativeMethods.CreateFile(LinkName, NativeMethods.FileAccessEx.None, FileShare.Read, IntPtr.Zero, FileMode.Open, flags, IntPtr.Zero)) { if (handle.IsInvalid) { throw Win32Utils.GetExceptionForLastError(); } var reparseDataBuffer = new NativeMethods.ReparseDataBuffer(); const int fsctlGetReparsePoint = 0x000900A8; var bytesReturned = 0; var success = NativeMethods.DeviceIoControl(handle, fsctlGetReparsePoint, IntPtr.Zero, 0, reparseDataBuffer, Marshal.SizeOf(typeof(NativeMethods.ReparseDataBuffer)), ref bytesReturned, IntPtr.Zero); if (!success) { const uint pathNotAReparsePointError = 0x80071126; if ((uint)Marshal.GetHRForLastWin32Error() == pathNotAReparsePointError) { return(null); } throw Win32Utils.GetExceptionForLastError(); } return(reparseDataBuffer); } }
/// <summary> /// Creates a junction point from the specified directory to the specified target directory. /// </summary> /// <remarks> /// Only works on NTFS. /// </remarks> /// <param name="targetDir">The target directory to create</param> /// <param name="sourceDir">The source directory to alias</param> /// <param name="overwrite">If true overwrites an existing reparse point or empty directory</param> /// <exception cref="IOException">Thrown when the junction point could not be created or when /// an existing directory was found and <paramref name="overwrite" /> if false</exception> public static void Create(string sourceDir, string targetDir, bool overwrite) { sourceDir = Path.GetFullPath(sourceDir); if (!Directory.Exists(sourceDir)) { throw new IOException("Source path does not exist or is not a directory."); } if (Directory.Exists(targetDir)) { if (!overwrite) { throw new IOException("Directory '{targetDir}' already exists."); } } else { Directory.CreateDirectory(targetDir); } using (SafeFileHandle handle = OpenReparsePoint(targetDir, NativeMethods.FileAccess.GenericWrite)) { byte[] sourceDirBytes = Encoding.Unicode.GetBytes(NativeMethods.NonInterpretedPathPrefix + Path.GetFullPath(sourceDir)); NativeMethods.ReparseDataBuffer reparseDataBuffer = new NativeMethods.ReparseDataBuffer(); reparseDataBuffer.ReparseTag = NativeMethods.IoReparseTagMountPoint; reparseDataBuffer.ReparseDataLength = (ushort)(sourceDirBytes.Length + 12); reparseDataBuffer.SubstituteNameOffset = 0; reparseDataBuffer.SubstituteNameLength = (ushort)sourceDirBytes.Length; reparseDataBuffer.PrintNameOffset = (ushort)(sourceDirBytes.Length + 2); reparseDataBuffer.PrintNameLength = 0; reparseDataBuffer.PathBuffer = new byte[0x3ff0]; Array.Copy(sourceDirBytes, reparseDataBuffer.PathBuffer, sourceDirBytes.Length); int inBufferSize = Marshal.SizeOf(reparseDataBuffer); IntPtr inBuffer = Marshal.AllocHGlobal(inBufferSize); try { Marshal.StructureToPtr(reparseDataBuffer, inBuffer, false); int bytesReturned; bool result = NativeMethods.DeviceIoControl(handle.DangerousGetHandle(), NativeMethods.FsctlSetReparsePoint, inBuffer, sourceDirBytes.Length + 20, IntPtr.Zero, 0, out bytesReturned, IntPtr.Zero); if (!result) { ThrowLastWin32Error("Unable to create junction point '{sourceDir}' -> '{targetDir}'."); } } finally { Marshal.FreeHGlobal(inBuffer); } } }
/// <summary> /// Deletes a junction point at the specified source directory along with the directory itself. /// Does nothing if the junction point does not exist. /// </summary> /// <remarks> /// Only works on NTFS. /// </remarks> /// <param name="junctionPoint">The junction point path</param> public static void Delete(string junctionPoint) { if (!Directory.Exists(junctionPoint)) { if (File.Exists(junctionPoint)) { throw new IOException("Path is not a junction point."); } return; } using (SafeFileHandle handle = OpenReparsePoint(junctionPoint, NativeMethods.FileAccess.GenericWrite)) { NativeMethods.ReparseDataBuffer reparseDataBuffer = new NativeMethods.ReparseDataBuffer(); reparseDataBuffer.ReparseTag = NativeMethods.IoReparseTagMountPoint; reparseDataBuffer.ReparseDataLength = 0; reparseDataBuffer.PathBuffer = new byte[0x3ff0]; int inBufferSize = Marshal.SizeOf(reparseDataBuffer); IntPtr inBuffer = Marshal.AllocHGlobal(inBufferSize); try { Marshal.StructureToPtr(reparseDataBuffer, inBuffer, false); int bytesReturned; bool result = NativeMethods.DeviceIoControl(handle.DangerousGetHandle(), NativeMethods.FsctlDeleteReparsePoint, inBuffer, 8, IntPtr.Zero, 0, out bytesReturned, IntPtr.Zero); if (!result) { ThrowLastWin32Error("Unable to delete junction point."); } } finally { Marshal.FreeHGlobal(inBuffer); } try { Directory.Delete(junctionPoint); } catch (IOException ex) { throw new IOException("Unable to delete junction point.", ex); } } }
/// <summary> /// /// </summary> /// <param name="handle"></param> /// <returns></returns> private static string InternalGetTarget(SafeFileHandle handle) { int outBufferSize = Marshal.SizeOf(typeof(NativeMethods.ReparseDataBuffer)); IntPtr outBuffer = Marshal.AllocHGlobal(outBufferSize); try { int bytesReturned; bool result = NativeMethods.DeviceIoControl(handle.DangerousGetHandle(), NativeMethods.FsctlGetReparsePoint, IntPtr.Zero, 0, outBuffer, outBufferSize, out bytesReturned, IntPtr.Zero); if (!result) { int error = Marshal.GetLastWin32Error(); if (error == NativeMethods.ErrorNotAReparsePoint) { return(null); } ThrowLastWin32Error("Unable to get information about junction point."); } NativeMethods.ReparseDataBuffer reparseDataBuffer = (NativeMethods.ReparseDataBuffer) Marshal.PtrToStructure(outBuffer, typeof(NativeMethods.ReparseDataBuffer)); if (reparseDataBuffer.ReparseTag != NativeMethods.IoReparseTagMountPoint) { return(null); } string targetDir = Encoding.Unicode.GetString(reparseDataBuffer.PathBuffer, reparseDataBuffer.SubstituteNameOffset, reparseDataBuffer.SubstituteNameLength); if (targetDir.StartsWith(NativeMethods.NonInterpretedPathPrefix)) { targetDir = targetDir.Substring(NativeMethods.NonInterpretedPathPrefix.Length); } return(targetDir); } finally { Marshal.FreeHGlobal(outBuffer); } }