public static string GetDestination(string path) { if (string.IsNullOrEmpty(path)) { throw new ArgumentNullException(nameof(path)); } FileAttributes attributes; if (!Kernel32.TryGetFileAttributes(path, out attributes)) { throw new IOException($"The path '{path}' does not exist."); } if ((attributes & JunctionAttributes) == JunctionAttributes) { ReparseDataBuffer data = GetReparseData(path); if (data.ReparseTag == ReparseTagType.MountPoint) { string destination = Encoding.Unicode.GetString(data.PathBuffer, data.SubstituteNameOffset, data.SubstituteNameLength); if (destination.StartsWith(DestinationPrefix)) { destination = destination.Substring(DestinationPrefix.Length); } return(destination); } } throw new IOException($"The path '{path}' does not point to a valid directory junction."); }
/// <summary> /// Creates a junction point from the specified directory to the specified target directory. /// </summary> /// <remarks> /// Only works on NTFS. /// </remarks> /// <param name="junctionPoint">The junction point path</param> /// <param name="targetDir">The target directory</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 junctionPoint, string targetDir, bool overwrite) { targetDir = Path.GetFullPath(targetDir); if (!Directory.Exists(targetDir)) { throw new IOException("Target path does not exist or is not a directory."); } if (Directory.Exists(junctionPoint)) { if (!overwrite) { throw new IOException("Directory already exists and overwrite parameter is false."); } } else { Directory.CreateDirectory(junctionPoint); } using (SafeFileHandle handle = OpenReparsePoint(junctionPoint, EFileAccess.GenericWrite)) { byte[] targetDirBytes = Encoding.Unicode.GetBytes(NON_INTERPRETED_PATH_PREFIX + Path.GetFullPath(targetDir)); ReparseDataBuffer reparseDataBuffer = new ReparseDataBuffer(); reparseDataBuffer.ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; reparseDataBuffer.ReparseDataLength = (ushort)(targetDirBytes.Length + 12); reparseDataBuffer.SubstituteNameOffset = 0; reparseDataBuffer.SubstituteNameLength = (ushort)targetDirBytes.Length; reparseDataBuffer.PrintNameOffset = (ushort)(targetDirBytes.Length + 2); reparseDataBuffer.PrintNameLength = 0; reparseDataBuffer.PathBuffer = new byte[0x3ff0]; Array.Copy(targetDirBytes, reparseDataBuffer.PathBuffer, targetDirBytes.Length); int inBufferSize = Marshal.SizeOf(reparseDataBuffer); IntPtr inBuffer = Marshal.AllocHGlobal(inBufferSize); try { Marshal.StructureToPtr(reparseDataBuffer, inBuffer, false); int bytesReturned; bool result = DeviceIoControl(handle.DangerousGetHandle(), FSCTL_SET_REPARSE_POINT, inBuffer, targetDirBytes.Length + 20, IntPtr.Zero, 0, out bytesReturned, IntPtr.Zero); if (!result) { ThrowLastWin32Error("Unable to create junction point."); } } 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, EFileAccess.GenericWrite)) { ReparseDataBuffer reparseDataBuffer = new ReparseDataBuffer(); reparseDataBuffer.ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; 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 = DeviceIoControl(handle.DangerousGetHandle(), FSCTL_DELETE_REPARSE_POINT, 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); } } }
public static void SetDestination(string path, string destination) { if (string.IsNullOrEmpty(path)) { throw new ArgumentNullException(nameof(path)); } if (string.IsNullOrEmpty(destination)) { throw new ArgumentNullException(nameof(destination)); } FileAttributes attributes; if (!Kernel32.TryGetFileAttributes(path, out attributes)) { throw new IOException($"The path '{path}' does not exist."); } if ((attributes & FileAttributes.Directory) == FileAttributes.Directory) { destination = Path.GetFullPath(destination); string print = destination; destination = DestinationPrefix + destination; int destinationLength = Encoding.Unicode.GetByteCount(destination); int printLength = Encoding.Unicode.GetByteCount(print); var data = new ReparseDataBuffer() { ReparseTag = ReparseTagType.MountPoint, ReparseDataLength = (ushort)(4 * sizeof(ushort) + destinationLength + sizeof(char) + printLength + sizeof(char)), SubstituteNameOffset = 0, SubstituteNameLength = (ushort)destinationLength, PrintNameOffset = (ushort)(destinationLength + sizeof(char)), PrintNameLength = (ushort)printLength, PathBuffer = new byte[0x3FF0] }; Encoding.Unicode.GetBytes(destination, 0, destination.Length, data.PathBuffer, 0); Encoding.Unicode.GetBytes(print, 0, print.Length, data.PathBuffer, destinationLength + sizeof(char)); SetReparseData(path, data); return; } throw new IOException($"The path '{path}' does not point to a valid directory."); }
private static void SetReparseData(string path, ReparseDataBuffer data) { using SafeFileHandle reparsePointHandle = OpenReparsePoint(path, true); IntPtr buffer = Marshal.AllocHGlobal(Marshal.SizeOf(data)); try { Marshal.StructureToPtr(data, buffer, false); Kernel32.DeviceIOControl(reparsePointHandle, IOControlCode.FsctlSetReparsePoint, buffer, ReparseDataBuffer.HeaderSize + data.ReparseDataLength, IntPtr.Zero, 0, out _); } finally { Marshal.FreeHGlobal(buffer); } }
internal static void CreateReparsePoint(string junctionPoint, string targetDirectoryPath) { using (var handle = OpenReparsePoint(junctionPoint, EFileAccess.GenericWrite)) { var targetDirBytes = Encoding.Unicode.GetBytes(_nonInterpretedPathPrefix + Path.GetFullPath(targetDirectoryPath)); var reparseDataBuffer = new ReparseDataBuffer(); reparseDataBuffer.ReparseTag = _reparseTagMountPoint; reparseDataBuffer.ReparseDataLength = (ushort)(targetDirBytes.Length + 12); reparseDataBuffer.SubstituteNameOffset = 0; reparseDataBuffer.SubstituteNameLength = (ushort)targetDirBytes.Length; reparseDataBuffer.PrintNameOffset = (ushort)(targetDirBytes.Length + 2); reparseDataBuffer.PrintNameLength = 0; reparseDataBuffer.PathBuffer = new byte[0x3ff0]; Array.Copy(targetDirBytes, reparseDataBuffer.PathBuffer, targetDirBytes.Length); var inBufferSize = Marshal.SizeOf(reparseDataBuffer); var inBuffer = Marshal.AllocHGlobal(inBufferSize); try { Marshal.StructureToPtr(reparseDataBuffer, inBuffer, false); int bytesReturned; var result = DeviceIoControl( handle.DangerousGetHandle(), _setReparsePointCommand, inBuffer, targetDirBytes.Length + 20, IntPtr.Zero, 0, out bytesReturned, IntPtr.Zero); if (!result) { ThrowLastWin32Error("Unable to create junction point."); } } finally { Marshal.FreeHGlobal(inBuffer); } } }
private static string InternalGetTarget(SafeFileHandle handle) { int outBufferSize = Marshal.SizeOf(typeof(ReparseDataBuffer)); IntPtr outBuffer = Marshal.AllocHGlobal(outBufferSize); try { int bytesReturned; bool result = DeviceIoControl(handle.DangerousGetHandle(), FSCTL_GET_REPARSE_POINT, IntPtr.Zero, 0, outBuffer, outBufferSize, out bytesReturned, IntPtr.Zero); if (!result) { int error = Marshal.GetLastWin32Error(); if (error == ERROR_NOT_A_REPARSE_POINT) { return(null); } ThrowLastWin32Error("Unable to get information about junction point."); } ReparseDataBuffer reparseDataBuffer = (ReparseDataBuffer) Marshal.PtrToStructure(outBuffer, typeof(ReparseDataBuffer)); if (reparseDataBuffer.ReparseTag != IO_REPARSE_TAG_MOUNT_POINT) { return(null); } string targetDir = Encoding.Unicode.GetString(reparseDataBuffer.PathBuffer, reparseDataBuffer.SubstituteNameOffset, reparseDataBuffer.SubstituteNameLength); if (targetDir.StartsWith(NON_INTERPRETED_PATH_PREFIX)) { targetDir = targetDir.Substring(NON_INTERPRETED_PATH_PREFIX.Length); } return(targetDir); } finally { Marshal.FreeHGlobal(outBuffer); } }
private static ReparseDataBuffer GetReparseData(string path) { using SafeFileHandle reparsePointHandle = OpenReparsePoint(path, false); IntPtr buffer = Marshal.AllocHGlobal(ReparseDataBuffer.MaximumSize); try { Kernel32.DeviceIOControl(reparsePointHandle, IOControlCode.FsctlGetReparsePoint, IntPtr.Zero, 0, buffer, ReparseDataBuffer.MaximumSize, out _); ReparseDataBuffer data = Marshal.PtrToStructure <ReparseDataBuffer>(buffer); return(data); } finally { Marshal.FreeHGlobal(buffer); } }
/// <summary> /// Determines whether the given path refers to an existing directory junction on disk. /// </summary> /// <param name="path">The path to test.</param> /// <returns>Returns true if path refers to an existing directory junction, othwerwise false.</returns> public static bool Exists(string path) { if (string.IsNullOrEmpty(path)) { throw new ArgumentNullException(nameof(path)); } if (!Kernel32.TryGetFileAttributes(path, out var attributes)) { return(false); } if ((attributes & JunctionAttributes) != JunctionAttributes) { return(false); } ReparseDataBuffer data = GetReparseData(path); return(data.ReparseTag == ReparseTagType.MountPoint); }
private static extern bool DeviceIoControl(IntPtr hDevice, uint dwIoControlCode, IntPtr lpInBuffer, uint nInBufferSize, out ReparseDataBuffer outBuffer, uint nOutBufferSize, out uint lpBytesReturned, IntPtr lpOverlapped);
private static extern bool DeviceIoControl(IntPtr hDevice, uint dwIoControlCode, IntPtr lpInBuffer, uint nInBufferSize, out ReparseDataBuffer outBuffer, uint nOutBufferSize, out uint lpBytesReturned, IntPtr lpOverlapped);