Exemplo n.º 1
0
        public static string GetTarget(string fileName)
        {
            SafeFileHandle handle = new SafeFileHandle(CreateFile(fileName,
                                                                  DESIRED_ACCESS, SHARE_MODE, IntPtr.Zero,
                                                                  CREATION_DISPOSITION, FLAGS_AND_ATTRIBUTES, IntPtr.Zero),
                                                       true);

            if (Marshal.GetLastWin32Error() != 0)
            {
                string message = new Win32Exception(Marshal.GetLastWin32Error()).Message;
                throw new IOException(message + " (" + fileName + ")",
                                      Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error()));
            }

            int    outBufferSize = Marshal.SizeOf(typeof(REPARSE_DATA_BUFFER));
            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)
                {
                    if (Marshal.GetLastWin32Error() == ERROR_NOT_A_REPARSE_POINT)
                    {
                        return(null);
                    }
                    string message = new Win32Exception(Marshal.GetLastWin32Error()).Message;
                    throw new IOException(message + " (" + fileName + ")",
                                          Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error()));
                }

                REPARSE_DATA_BUFFER reparseDataBuffer = (REPARSE_DATA_BUFFER)
                                                        Marshal.PtrToStructure(outBuffer, typeof(REPARSE_DATA_BUFFER));

                ushort offset = reparseDataBuffer.PrintNameOffset;
                if (reparseDataBuffer.ReparseTag == IO_REPARSE_TAG_SYMLINK)
                {
                    // + sizeof(uint) for Flags
                    // which I don't have in REPARSE_DATA_BUFFER
                    offset += sizeof(uint);
                }
                else if (reparseDataBuffer.ReparseTag != IO_REPARSE_TAG_MOUNT_POINT)
                {
                    // throw an error?
                }
                string targetDir = Encoding.Unicode.GetString(reparseDataBuffer.PathBuffer,
                                                              offset, reparseDataBuffer.PrintNameLength);

                return(targetDir);
            }
            finally
            {
                handle.Close();
                Marshal.FreeHGlobal(outBuffer);
            }
        }
Exemplo n.º 2
0
        /// <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(NonInterpretedPathPrefix + Path.GetFullPath(targetDir));

                REPARSE_DATA_BUFFER reparseDataBuffer = new REPARSE_DATA_BUFFER
                {
                    ReparseTag           = IO_REPARSE_TAG_MOUNT_POINT,
                    ReparseDataLength    = (ushort)(targetDirBytes.Length + 12),
                    SubstituteNameOffset = 0,
                    SubstituteNameLength = (ushort)targetDirBytes.Length,
                    PrintNameOffset      = (ushort)(targetDirBytes.Length + 2),
                    PrintNameLength      = 0,
                    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);
                }
            }
        }
Exemplo n.º 3
0
 // https://github.com/rad1oactive/BetterExplorer/blob/master/Windows%20API%20Code%20Pack%201.1/source/WindowsAPICodePack/Shell/ReparsePoint.cs
 public static string ParseSymLink(string path)
 {
     using var handle = OpenFileForRead(path, false, 0x00200000);
     if (!handle.IsInvalid)
     {
         REPARSE_DATA_BUFFER buffer = new REPARSE_DATA_BUFFER();
         if (DeviceIoControl(handle.DangerousGetHandle(), FSCTL_GET_REPARSE_POINT, IntPtr.Zero, 0, out buffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, out _, IntPtr.Zero))
         {
             var subsString       = new string(buffer.PathBuffer, ((buffer.SubsNameOffset / 2) + 2), buffer.SubsNameLength / 2);
             var printString      = new string(buffer.PathBuffer, ((buffer.PrintNameOffset / 2) + 2), buffer.PrintNameLength / 2);
             var normalisedTarget = printString ?? subsString;
             if (string.IsNullOrEmpty(normalisedTarget))
             {
                 normalisedTarget = subsString;
                 if (normalisedTarget.StartsWith(@"\??\", StringComparison.Ordinal))
                 {
                     normalisedTarget = normalisedTarget.Substring(4);
                 }
             }
             if (buffer.ReparseTag == IO_REPARSE_TAG_SYMLINK && (normalisedTarget.Length < 2 || normalisedTarget[1] != ':'))
             {
                 // Target is relative, get the absolute path
                 normalisedTarget = normalisedTarget.TrimStart(Path.DirectorySeparatorChar);
                 path             = path.TrimEnd(Path.DirectorySeparatorChar);
                 normalisedTarget = Path.GetFullPath(Path.Combine(path.Substring(0, path.LastIndexOf(Path.DirectorySeparatorChar)), normalisedTarget));
             }
             return(normalisedTarget);
         }
     }
     return(null);
 }
Exemplo n.º 4
0
        public static bool IsSymLink(string path)
        {
            using (SafeFileHandle output = CreateFile(
                       path,
                       FileAccess.FILE_READ_ATTRIBUTES,
                       FileShare.Read,
                       IntPtr.Zero,
                       FileMode.Open,
                       FileAttributes.FILE_FLAG_BACKUP_SEMANTICS | FileAttributes.FILE_FLAG_OPEN_REPARSE_POINT,
                       IntPtr.Zero))
            {
                if (output.IsInvalid)
                {
                    ThrowLastWin32Exception($"Invalid handle for '{path}' as symlink");
                }

                REPARSE_DATA_BUFFER reparseData = new REPARSE_DATA_BUFFER();
                reparseData.ReparseDataLength = (4 * sizeof(ushort)) + ReparseDataPathBufferLength;
                uint bytesReturned;
                if (!DeviceIoControl(output, FSCTL_GET_REPARSE_POINT, IntPtr.Zero, 0, out reparseData, (uint)Marshal.SizeOf(reparseData), out bytesReturned, IntPtr.Zero))
                {
                    ThrowLastWin32Exception($"Failed to place reparse point for '{path}'");
                }

                return(reparseData.ReparseTag == IO_REPARSE_TAG_SYMLINK || reparseData.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT);
            }
        }
        /// <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="System.IO.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 = false, bool makeReadOnly = false)
        {
            sourceDir = Path.GetFullPath(sourceDir);

            if (!Directory.Exists(sourceDir))
            {
                throw new System.IO.IOException($"Source path does not exist or is not a directory.");
            }

            if (Directory.Exists(targetDir) && !overwrite)
            {
                throw new System.IO.IOException($"Directory '{targetDir}' already exists.");
            }

            Directory.CreateDirectory(targetDir);

            using (SafeFileHandle handle = OpenReparsePoint(targetDir, EFileAccess.GenericWrite))
            {
                byte[] sourceDirBytes = Encoding.Unicode.GetBytes(NonInterpretedPathPrefix + Path.GetFullPath(sourceDir));

                REPARSE_DATA_BUFFER reparseDataBuffer = new REPARSE_DATA_BUFFER();

                reparseDataBuffer.ReparseTag           = IO_REPARSE_TAG_MOUNT_POINT;
                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 = DeviceIoControl(handle.DangerousGetHandle(), FSCTL_SET_REPARSE_POINT,
                                                  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);
                }
            }

            if (makeReadOnly)
            {
                File.SetAttributes(targetDir, System.IO.FileAttributes.ReadOnly);
            }
        }
Exemplo n.º 6
0
        /// <summary>
        /// 为目标目录创建一个指向目标目录的目录联接。
        /// </summary>
        /// <remarks>
        /// 注意,此方法仅在 NTFS 分区上才会生效。
        /// </remarks>
        /// <param name="junctionPoint">目录联接的路径。</param>
        /// <param name="targetDirectory">要联接的目标目录。</param>
        /// <param name="overwrite">If true overwrites an existing reparse point or empty directory</param>
        /// <exception cref="IOException">
        /// 如果无法创建目录联接则抛出异常。或者 <paramref name="overwrite" /> 为 false 时,当前目录联接路径已经存在。
        /// </exception>
        public static void Create(string junctionPoint, string targetDirectory, bool overwrite)
        {
            targetDirectory = Path.GetFullPath(targetDirectory);

            if (!Directory.Exists(targetDirectory))
            {
                throw new IOException($"{nameof(targetDirectory)} 指定的目标目录不存在,路径是“{targetDirectory}”。");
            }

            if (Directory.Exists(junctionPoint))
            {
                if (!overwrite)
                {
                    throw new IOException($"目录联接“{junctionPoint}”已经存在,如果需要替换,请设置 {nameof(overwrite)} 为 true。");
                }
            }
            else
            {
                Directory.CreateDirectory(junctionPoint);
            }

            using (SafeFileHandle handle = OpenReparsePoint(junctionPoint, EFileAccess.GenericWrite))
            {
                var targetDirectoryBytes = Encoding.Unicode.GetBytes(NonInterpretedPathPrefix + Path.GetFullPath(targetDirectory));

                var reparseDataBuffer = new REPARSE_DATA_BUFFER
                {
                    ReparseTag           = IO_REPARSE_TAG_MOUNT_POINT,
                    ReparseDataLength    = (ushort)(targetDirectoryBytes.Length + 12),
                    SubstituteNameOffset = 0,
                    SubstituteNameLength = (ushort)targetDirectoryBytes.Length,
                    PrintNameOffset      = (ushort)(targetDirectoryBytes.Length + 2),
                    PrintNameLength      = 0,
                    PathBuffer           = new byte[0x3ff0]
                };
                Array.Copy(targetDirectoryBytes, reparseDataBuffer.PathBuffer, targetDirectoryBytes.Length);

                var inBufferSize = Marshal.SizeOf(reparseDataBuffer);
                var inBuffer     = Marshal.AllocHGlobal(inBufferSize);

                try
                {
                    Marshal.StructureToPtr(reparseDataBuffer, inBuffer, false);

                    var result = DeviceIoControl(handle.DangerousGetHandle(), FSCTL_SET_REPARSE_POINT,
                                                 inBuffer, targetDirectoryBytes.Length + 20, IntPtr.Zero, 0, out var bytesReturned, IntPtr.Zero);

                    if (!result)
                    {
                        ThrowLastWin32Error("无法创建目录联接。");
                    }
                }
                finally
                {
                    Marshal.FreeHGlobal(inBuffer);
                }
            }
        }
Exemplo n.º 7
0
        /// <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 DeleteJunctionOrSymlink(string junctionPoint)
        {
            if (!Directory.Exists(junctionPoint))
            {
                if (File.Exists(junctionPoint))
                {
                    throw new IOException("Path is not a junction point.");
                }

                return;
            }

            var linkManager = ReparsePointFactory.Create();

            if (linkManager.GetLinkType(junctionPoint) == LinkType.Symbolic)
            {
                Directory.Delete(junctionPoint, false);
                return;
            }

            using (SafeFileHandle handle = OpenReparsePoint(junctionPoint, EFileAccess.GenericWrite))
            {
                REPARSE_DATA_BUFFER reparseDataBuffer = new REPARSE_DATA_BUFFER();

                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);
                }
            }
        }
Exemplo n.º 8
0
 private static extern bool DeviceIoControl(
     SafeFileHandle hDevice,
     uint IoControlCode,
     [In] IntPtr InBuffer,
     uint nInBufferSize,
     [Out] out REPARSE_DATA_BUFFER OutBuffer,
     uint nOutBufferSize,
     out uint pBytesReturned,
     [In] IntPtr Overlapped);
Exemplo n.º 9
0
        /// <summary>
        ///     Creates a junction point to the specified target directory.
        /// </summary>
        /// <remarks>
        ///     Only works on NTFS.
        /// </remarks>
        /// <param name="junctionPoint">The directory to create</param>
        /// <param name="destination">The existing directory</param>
        /// <param name="overwrite">If true overwrites an existing reparse point or empty directory</param>
        /// <exception cref="System.IO.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 destination, bool overwrite)
        {
            if (destination.StartsWith(@"\\?\Volume{"))
            {
                throw new ArgumentException("Volume name is not supported!");                                                    // TODO support "mounted folder"
            }
            junctionPoint = Path.GetFullPath(junctionPoint);
            destination   = Path.GetFullPath(destination);

            if (!Directory.Exists(destination))
            {
                throw Helper.IOException($"Destination path does not exist or is not a directory.");
            }

            if (Directory.Exists(junctionPoint) && overwrite == false)
            {
                throw Helper.IOException($"Directory '{junctionPoint}' already exists.");
            }

            // try { Directory.Delete(reparsePoint);} catch (Exception ex) {}
            Directory.CreateDirectory(junctionPoint);

            using (var handle = OpenReparsePoint(junctionPoint, EFileAccess.GenericWrite)) {
                var sourceDirBytes = Encoding.Unicode.GetBytes(NonInterpretedPathPrefix + destination);

                var reparseDataBuffer = new REPARSE_DATA_BUFFER {
                    ReparseTag           = IO_REPARSE_TAG_MOUNT_POINT,
                    ReparseDataLength    = (ushort)(sourceDirBytes.Length + 12),
                    SubstituteNameOffset = 0,
                    SubstituteNameLength = (ushort)sourceDirBytes.Length,
                    PrintNameOffset      = (ushort)(sourceDirBytes.Length + 2),
                    PrintNameLength      = 0,
                    PathBuffer           = new byte[0x3ff0]
                };

                Array.Copy(sourceDirBytes, reparseDataBuffer.PathBuffer, sourceDirBytes.Length);

                var inBufferSize = Marshal.SizeOf(reparseDataBuffer);
                var inBuffer     = Marshal.AllocHGlobal(inBufferSize);

                try {
                    Marshal.StructureToPtr(reparseDataBuffer, inBuffer, false);

                    int bytesReturned;
                    var result = DeviceIoControl(handle.DangerousGetHandle(), FSCTL_SET_REPARSE_POINT, inBuffer, sourceDirBytes.Length + 20, IntPtr.Zero, 0, out bytesReturned, IntPtr.Zero);

                    if (!result)
                    {
                        ThrowLastWin32Error($"Unable to create junction point '{destination}' -> '{junctionPoint}'.");
                    }
                }
                finally {
                    Marshal.FreeHGlobal(inBuffer);
                }
            }
        }
Exemplo n.º 10
0
 private static extern bool DeviceIoControl(
     IntPtr hDevice,
     uint dwIoControlCode,
     IntPtr lpInBuffer,
     uint nInBufferSize,
     //IntPtr lpOutBuffer,
     out REPARSE_DATA_BUFFER outBuffer,
     uint nOutBufferSize,
     out uint lpBytesReturned,
     IntPtr lpOverlapped);
Exemplo n.º 11
0
		static extern bool DeviceIoControl(
			IntPtr hDevice,
			uint dwIoControlCode,
			IntPtr lpInBuffer,
			uint nInBufferSize,
			//IntPtr lpOutBuffer, 
			out REPARSE_DATA_BUFFER outBuffer,
			uint nOutBufferSize,
			out uint lpBytesReturned,
			IntPtr lpOverlapped);
Exemplo n.º 12
0
        /// <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("SourceDirectory already exists and overwrite parameter is false.");
            }
            else
            {
                Directory.CreateDirectory(junctionPoint);
            }

            using (SafeFileHandle handle = OpenReparsePoint(junctionPoint, EFileAccess.GenericWrite))
            {
                var targetDirBytes = Encoding.Unicode.GetBytes(NonInterpretedPathPrefix + Path.GetFullPath(targetDir));

                var reparseDataBuffer = new REPARSE_DATA_BUFFER
                {
                    ReparseTag = IO_REPARSE_TAG_MOUNT_POINT,
                    ReparseDataLength = (ushort) (targetDirBytes.Length + 12),
                    SubstituteNameOffset = 0,
                    SubstituteNameLength = (ushort) targetDirBytes.Length,
                    PrintNameOffset = (ushort) (targetDirBytes.Length + 2),
                    PrintNameLength = 0,
                    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(), 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 (var handle = OpenReparsePoint(junctionPoint, EFileAccess.GenericWrite))
            {
                var reparseDataBuffer = new REPARSE_DATA_BUFFER
                {
                    ReparseTag        = IO_REPARSE_TAG_MOUNT_POINT,
                    ReparseDataLength = 0,
                    PathBuffer        = new byte[0x3ff0]
                };


                var inBufferSize = Marshal.SizeOf(reparseDataBuffer);
                var inBuffer     = Marshal.AllocHGlobal(inBufferSize);
                try
                {
                    Marshal.StructureToPtr(reparseDataBuffer, inBuffer, false);

                    int bytesReturned;
                    var 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);
                }
            }
        }
Exemplo n.º 14
0
        public static void CreateJunction(string link, string target)
        {
            var di = new DirectoryInfo(link);

            if (di.Exists)
            {
                if (di.GetFiles().Length > 0 || di.GetDirectories().Length > 0)
                {
                    throw new IOException("Directory already exists and is not empty");
                }
            }
            else
            {
                di.Create();
            }

            using (SafeFileHandle handle = OpenReparsePoint(link, EFileAccess.GenericWrite))
            {
                byte[] linkbytes = Encoding.Unicode.GetBytes(VIRTUAL_NTFS_PATH_PREFIX + Path.GetFullPath(target));

                REPARSE_DATA_BUFFER buffer = new REPARSE_DATA_BUFFER();
                buffer.ReparseTag           = IO_REPARSE_TAG_MOUNT_POINT;
                buffer.ReparseDataLength    = (ushort)(linkbytes.Length + 12);
                buffer.SubstituteNameOffset = 0;
                buffer.SubstituteNameLength = (ushort)linkbytes.Length;
                buffer.PrintNameOffset      = (ushort)(linkbytes.Length + 2);
                buffer.PrintNameLength      = 0;
                buffer.PathBuffer           = new byte[0x3ff0];
                Array.Copy(linkbytes, buffer.PathBuffer, linkbytes.Length);

                int    _size   = Marshal.SizeOf(buffer);
                IntPtr _buffer = Marshal.AllocHGlobal(_size);

                try
                {
                    Marshal.StructureToPtr(buffer, _buffer, false);

                    int  bytesReturned;
                    bool result = NativeMethods.DeviceIoControl(handle.DangerousGetHandle(), FSCTL_SET_REPARSE_POINT,
                                                                _buffer, linkbytes.Length + 20, IntPtr.Zero, 0, out bytesReturned, IntPtr.Zero);

                    if (!result)
                    {
                        throw new IOException("Failed to create junction", Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error()));
                    }
                }
                finally
                {
                    Marshal.FreeHGlobal(_buffer);
                }
            }
        }
Exemplo n.º 15
0
        private string GetTargetPath()
        {
            IntPtr buff     = IntPtr.Zero;
            int    dataSize = 0;

            SafeFileHandle handle = OpenFile();

            if (handle.IsInvalid)
            {
                return(null);
            }

            try {
                //
                // Try REPARSE data
                if (FindReparsePoint(handle, out buff, out dataSize))
                {
                    REPARSE_DATA_BUFFER reparseData = Marshal.PtrToStructure <REPARSE_DATA_BUFFER>(buff);

                    if (reparseData.ReparseTag == ReparseTagType.IO_REPARSE_TAG_SYMLINK ||
                        reparseData.ReparseTag == ReparseTagType.IO_REPARSE_TAG_MOUNT_POINT)
                    {
                        //
                        // Since the data size isn't fixed, Marshal.PtrToStructure can't return via reparseData.PathBuffer
                        // Therefore we need to marshal it manually from the native data
                        int    offset = Marshal.SizeOf <REPARSE_DATA_BUFFER>();
                        byte[] data   = new byte[dataSize];

                        Marshal.Copy(new IntPtr(buff.ToInt64() + offset), data, 0, data.Length);

                        //
                        // Symlink
                        if (reparseData.ReparseTag == ReparseTagType.IO_REPARSE_TAG_SYMLINK)
                        {
                            return(Encoding.Unicode.GetString(data, reparseData.PrintNameOffset, reparseData.PrintNameLength));
                        }

                        //
                        // Junction
                        return(Encoding.Unicode.GetString(data, reparseData.PrintNameOffset - Marshal.SizeOf <IntPtr>(), reparseData.PrintNameLength));
                    }
                }

                //
                // Fallback if REPARSE point can't be used
                return(GetFinalPathNameByHandle(handle));
            }
            finally {
                handle.Dispose();
                Marshal.FreeHGlobal(buff);
            }
        }
Exemplo n.º 16
0
        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))
            {
                REPARSE_DATA_BUFFER reparseDataBuffer = new REPARSE_DATA_BUFFER();

                reparseDataBuffer.ReparseTag        = 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 = DeviceIoControl(handle.DangerousGetHandle(), FsctlDeleteReparsePoint, inBuffer, 8, IntPtr.Zero, 0, out bytesReturned, IntPtr.Zero);

                    if (!result)
                    {
                        ThrowLastWin32Error(Strings.UnableToDeleteJunctionPoint);
                    }
                }
                finally
                {
                    Marshal.FreeHGlobal(inBuffer);
                }

                try
                {
                    Directory.Delete(junctionPoint);
                }
                catch (IOException ex)
                {
                    throw new IOException(Strings.UnableToDeleteJunctionPoint, ex);
                }
            }
        }
Exemplo n.º 17
0
        private static ReparsePointInfo InternalGetTarget(SafeFileHandle handle)
        {
            int    outBufferSize = Marshal.SizeOf(typeof(REPARSE_DATA_BUFFER));
            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.");
                }

                REPARSE_DATA_BUFFER reparseDataBuffer = (REPARSE_DATA_BUFFER)
                                                        Marshal.PtrToStructure(outBuffer, typeof(REPARSE_DATA_BUFFER));

                var res = new ReparsePointInfo
                {
                    ReparseTag = (ReparsePointTag)reparseDataBuffer.ReparseTag,
                    IsJunction = reparseDataBuffer.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT,
                };

                if (!res.IsJunction)
                {
                    return(res);
                }

                res.SubstituteName = Encoding.Unicode.GetString(reparseDataBuffer.PathBuffer,
                                                                reparseDataBuffer.SubstituteNameOffset, reparseDataBuffer.SubstituteNameLength);
                res.PrintName = Encoding.Unicode.GetString(reparseDataBuffer.PathBuffer,
                                                           reparseDataBuffer.PrintNameOffset, reparseDataBuffer.PrintNameLength);

                return(res);
            }
            finally
            {
                Marshal.FreeHGlobal(outBuffer);
            }
        }
Exemplo n.º 18
0
        private static string InternalGetTarget(SafeFileHandle handle)
        {
            int    outBufferSize = Marshal.SizeOf(typeof(REPARSE_DATA_BUFFER));
            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.");
                }

                REPARSE_DATA_BUFFER reparseDataBuffer = (REPARSE_DATA_BUFFER)
                                                        Marshal.PtrToStructure(outBuffer, typeof(REPARSE_DATA_BUFFER));

                if (reparseDataBuffer.ReparseTag != IO_REPARSE_TAG_MOUNT_POINT)
                {
                    return(null);
                }

                string targetDir = Encoding.Unicode.GetString(reparseDataBuffer.PathBuffer,
                                                              reparseDataBuffer.SubstituteNameOffset, reparseDataBuffer.SubstituteNameLength);

                if (targetDir.StartsWith(NonInterpretedPathPrefix))
                {
                    targetDir = targetDir.Substring(NonInterpretedPathPrefix.Length);
                }

                return(targetDir);
            }
            finally
            {
                Marshal.FreeHGlobal(outBuffer);
            }
        }
Exemplo n.º 19
0
        /// <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="target">The target directory</param>
        /// <exception cref="IOException">Thrown when the junction point could not be created or when
        public void Create(string junctionPoint, string target)
        {
            target = Path.GetFullPath(target);

            Directory.CreateDirectory(junctionPoint);

            using (SafeFileHandle handle = OpenReparsePoint(junctionPoint, EFileAccess.GenericWrite))
            {
                byte[] targetDirBytes = Encoding.Unicode.GetBytes(NonInterpretedPathPrefix + Path.GetFullPath(target));

                var reparseDataBuffer = new REPARSE_DATA_BUFFER();

                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);
                }
            }
        }
Exemplo n.º 20
0
        private static string InternalGetTarget(SafeFileHandle handle)
        {
            int outBufferSize = Marshal.SizeOf(typeof(REPARSE_DATA_BUFFER));

            IntPtr outBuffer = Marshal.AllocHGlobal(outBufferSize);

            try
            {
                int  bytesReturned;
                bool result = DeviceIoControl(handle.DangerousGetHandle(), FsctlGetReparsePoint, IntPtr.Zero, 0, outBuffer, outBufferSize, out bytesReturned, IntPtr.Zero);

                if (!result)
                {
                    int error = Marshal.GetLastWin32Error();
                    if (error == ErrorNotAReparsePoint)
                    {
                        return(null);
                    }

                    ThrowLastWin32Error(Strings.UnableToGetJunctionInformation);
                }

                REPARSE_DATA_BUFFER reparseDataBuffer = (REPARSE_DATA_BUFFER)
                                                        Marshal.PtrToStructure(outBuffer, typeof(REPARSE_DATA_BUFFER));

                if (reparseDataBuffer.ReparseTag != IoReparseTagMountPoint)
                {
                    return(null);
                }

                string targetDir = Encoding.Unicode.GetString(reparseDataBuffer.PathBuffer, reparseDataBuffer.SubstituteNameOffset, reparseDataBuffer.SubstituteNameLength);

                if (targetDir.StartsWith(NonInterpretedPathPrefix, StringComparison.Ordinal))
                {
                    targetDir = targetDir.Substring(NonInterpretedPathPrefix.Length);
                }

                return(targetDir);
            }
            finally
            {
                Marshal.FreeHGlobal(outBuffer);
            }
        }
Exemplo n.º 21
0
        private static void CreateJuntionWindows(string sourceDir, string targetDir)
        {
            Directory.CreateDirectory(targetDir);

            using (SafeFileHandle handle = OpenReparsePoint(targetDir, EFileAccess.GenericWrite))
            {
                byte[] sourceDirBytes = Encoding.Unicode.GetBytes(NonInterpretedPathPrefix + Path.GetFullPath(sourceDir));

                REPARSE_DATA_BUFFER reparseDataBuffer = new REPARSE_DATA_BUFFER
                {
                    ReparseTag           = IO_REPARSE_TAG_MOUNT_POINT,
                    ReparseDataLength    = (ushort)(sourceDirBytes.Length + 12),
                    SubstituteNameOffset = 0,
                    SubstituteNameLength = (ushort)sourceDirBytes.Length,
                    PrintNameOffset      = (ushort)(sourceDirBytes.Length + 2),
                    PrintNameLength      = 0,
                    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 = DeviceIoControl(handle.DangerousGetHandle(), FSCTL_SET_REPARSE_POINT, inBuffer: inBuffer, nInBufferSize: sourceDirBytes.Length + 20, outBuffer: IntPtr.Zero, nOutBufferSize: 0, pBytesReturned: out bytesReturned, lpOverlapped: IntPtr.Zero);

                    if (!result)
                    {
                        ThrowLastWin32Error($"Unable to create junction point \'{sourceDir}\' -> \'{targetDir}\'.");
                    }
                }
                finally
                {
                    Marshal.FreeHGlobal(inBuffer);
                }
            }
        }
Exemplo n.º 22
0
        public static void Create(string junctionPoint, string substituteName, string printName)
        {
            using (SafeFileHandle handle = OpenReparsePoint(junctionPoint, EFileAccess.GenericWrite))
            {
                byte[] substituteNameBytes = Encoding.Unicode.GetBytes(substituteName);
                byte[] printNameBytes      = Encoding.Unicode.GetBytes(printName);

                REPARSE_DATA_BUFFER reparseDataBuffer = new REPARSE_DATA_BUFFER();

                reparseDataBuffer.ReparseTag           = IO_REPARSE_TAG_MOUNT_POINT;
                reparseDataBuffer.ReparseDataLength    = (ushort)(substituteNameBytes.Length + printNameBytes.Length + 12);
                reparseDataBuffer.SubstituteNameOffset = 0;
                reparseDataBuffer.SubstituteNameLength = (ushort)substituteNameBytes.Length;
                reparseDataBuffer.PrintNameOffset      = (ushort)(substituteNameBytes.Length + 2);
                reparseDataBuffer.PrintNameLength      = (ushort)printNameBytes.Length;
                reparseDataBuffer.PathBuffer           = new byte[0x3ff0];
                Array.Copy(substituteNameBytes, 0, reparseDataBuffer.PathBuffer, reparseDataBuffer.SubstituteNameOffset, reparseDataBuffer.SubstituteNameLength);
                Array.Copy(printNameBytes, 0, reparseDataBuffer.PathBuffer, reparseDataBuffer.PrintNameOffset, reparseDataBuffer.PrintNameLength);

                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, substituteNameBytes.Length + printNameBytes.Length + 20, IntPtr.Zero, 0, out bytesReturned, IntPtr.Zero);

                    if (!result)
                    {
                        ThrowLastWin32Error("Unable to create junction point.");
                    }
                }
                finally
                {
                    Marshal.FreeHGlobal(inBuffer);
                }
            }
        }
Exemplo n.º 23
0
        public static string SymlinkTargetFromHandle(SafeFileHandle handle)
        {
            var rdb     = new REPARSE_DATA_BUFFER();
            var x       = 10;
            var outSize = new IntPtr(x);

            if (
                !DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, IntPtr.Zero, 0, out rdb, (UInt32)Marshal.SizeOf(rdb),
                                 out outSize, IntPtr.Zero))
            {
                throw new Win32Exception();
            }

            if (rdb.ReparseTag != IO_REPARSE_TAG_SYMLINK)
            {
                throw new ApplicationException("not a symlink");
            }

            var arrayOffset = rdb.PrintNameOffset / 2;

            return(rdb.PathBuffer.Substring(arrayOffset, arrayOffset + (rdb.PrintNameLength / 2)));
        }
Exemplo n.º 24
0
        /// <summary>
        /// Takes a full path to a reparse point and finds the target.
        /// </summary>
        /// <param name="path">Full path of the reparse point</param>
        public ReparsePoint(string path)
        {
            Debug.Assert(!string.IsNullOrEmpty(path) && path.Length > 2 && path[1] == ':' && path[2] == '\\');
            normalisedTarget = "";
            tag = TagType.None;
            bool success;
            int lastError;
            // Apparently we need to have backup privileges
            IntPtr token;
            TOKEN_PRIVILEGES tokenPrivileges = new TOKEN_PRIVILEGES();
            tokenPrivileges.Privileges = new LUID_AND_ATTRIBUTES[1];
            success = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, out token);
            lastError = Marshal.GetLastWin32Error();
            if (success) {
                success = LookupPrivilegeValue(null, SE_BACKUP_NAME, out tokenPrivileges.Privileges[0].Luid);			// null for local system
                lastError = Marshal.GetLastWin32Error();
                if (success) {
                    tokenPrivileges.PrivilegeCount = 1;
                    tokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
                    success = AdjustTokenPrivileges(token, false, ref tokenPrivileges, Marshal.SizeOf(tokenPrivileges), IntPtr.Zero, IntPtr.Zero);
                    lastError = Marshal.GetLastWin32Error();
                }
                CloseHandle(token);
            }

            if (success) {
                // Open the file and get its handle
                IntPtr handle = CreateFile(path, FileAccess.Read, FileShare.None, 0, FileMode.Open, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, IntPtr.Zero);
                lastError = Marshal.GetLastWin32Error();
                if (handle.ToInt32() >= 0) {
                    REPARSE_DATA_BUFFER buffer = new REPARSE_DATA_BUFFER();
                    // Make up the control code - see CTL_CODE on ntddk.h
                    uint controlCode = (FILE_DEVICE_FILE_SYSTEM << 16) | (FILE_ANY_ACCESS << 14) | (FSCTL_GET_REPARSE_POINT << 2) | METHOD_BUFFERED;
                    uint bytesReturned;
                    success = DeviceIoControl(handle, controlCode, IntPtr.Zero, 0, out buffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, out bytesReturned, IntPtr.Zero);
                    lastError = Marshal.GetLastWin32Error();
                    if (success) {
                        string subsString = "";
                        string printString = "";
                        // Note that according to http://wesnerm.blogs.com/net_undocumented/2006/10/symbolic_links_.html
                        // Symbolic links store relative paths, while junctions use absolute paths
                        // however, they can in fact be either, and may or may not have a leading \.
                        Debug.Assert(buffer.ReparseTag == IO_REPARSE_TAG_SYMLINK || buffer.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT,
                            "Unrecognised reparse tag");						// We only recognise these two
                        if (buffer.ReparseTag == IO_REPARSE_TAG_SYMLINK) {
                            // for some reason symlinks seem to have an extra two characters on the front
                            subsString = new string(buffer.ReparseTarget, (buffer.SubsNameOffset / 2 + 2), buffer.SubsNameLength / 2);
                            printString = new string(buffer.ReparseTarget, (buffer.PrintNameOffset / 2 + 2), buffer.PrintNameLength / 2);
                            tag = TagType.SymbolicLink;
                        } else if (buffer.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
                            // This could be a junction or a mounted drive - a mounted drive starts with "\\??\\Volume"
                            subsString = new string(buffer.ReparseTarget, buffer.SubsNameOffset / 2, buffer.SubsNameLength / 2);
                            printString = new string(buffer.ReparseTarget, buffer.PrintNameOffset / 2, buffer.PrintNameLength / 2);
                            tag = subsString.StartsWith(@"\??\Volume") ? TagType.MountPoint : TagType.JunctionPoint;
                        }
                        Debug.Assert(!(string.IsNullOrEmpty(subsString) && string.IsNullOrEmpty(printString)), "Failed to retrieve parse point");
                        // the printstring should give us what we want
                        if (!string.IsNullOrEmpty(printString)) {
                            normalisedTarget = printString;
                        } else {
                            // if not we can use the substring with a bit of tweaking
                            normalisedTarget = subsString;
                            Debug.Assert(normalisedTarget.Length > 2, "Target string too short");
                            Debug.Assert(
                                (normalisedTarget.StartsWith(@"\??\") && (normalisedTarget[5] == ':' || normalisedTarget.StartsWith(@"\??\Volume")) ||
                                (!normalisedTarget.StartsWith(@"\??\") && normalisedTarget[1] != ':')),
                                "Malformed subsString");
                            // Junction points must be absolute
                            Debug.Assert(
                                    buffer.ReparseTag == IO_REPARSE_TAG_SYMLINK ||
                                    normalisedTarget.StartsWith(@"\??\Volume") ||
                                    normalisedTarget[1] == ':',
                                "Relative junction point");
                            if (normalisedTarget.StartsWith(@"\??\")) {
                                normalisedTarget = normalisedTarget.Substring(4);
                            }
                        }
                        actualTarget = normalisedTarget;
                        // Symlinks can be relative.
                        if (buffer.ReparseTag == IO_REPARSE_TAG_SYMLINK && (normalisedTarget.Length < 2 || normalisedTarget[1] != ':')) {
                            // it's relative, we need to tack it onto the path
                            if (normalisedTarget[0] == '\\') {
                                normalisedTarget = normalisedTarget.Substring(1);
                            }
                            if (path.EndsWith(@"\")) {
                                path = path.Substring(0, path.Length - 1);
                            }
                            // Need to take the symlink name off the path
                            normalisedTarget = path.Substring(0, path.LastIndexOf('\\')) + @"\" + normalisedTarget;
                            // Note that if the symlink target path contains any ..s these are not normalised but returned as is.
                        }
                        // Remove any final slash for consistency
                        if (normalisedTarget.EndsWith("\\")) {
                            normalisedTarget = normalisedTarget.Substring(0, normalisedTarget.Length - 1);
                        }
                    }
                    CloseHandle(handle);
                } else {
                    success = false;
                }
            }
        }
Exemplo n.º 25
0
        /// <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))
            {
                REPARSE_DATA_BUFFER reparseDataBuffer = new REPARSE_DATA_BUFFER();

                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);
                }
            }
        }
Exemplo n.º 26
0
        public static void CreateJunction(string name, string target)
        {
            // If the target doesn't include a full path, assume relative to the current directory.
            if (!Path.IsPathRooted(target))
            {
                target = Path.Combine(Directory.GetCurrentDirectory(), target);
            }
            // likewise if the link name omits a path, make it relative to the current directory.
            if (!Path.IsPathRooted(name))
            {
                name = Path.Combine(Directory.GetCurrentDirectory(), name);
            }
            // Check target exists before trying to create a link
            if (!Directory.Exists(target))
            {
                throw new CreationFailedException("Target directory " + target + " does not exist");
            }

            // Create a directory to populate with our reparse point
            // If it isn't empty, create file will return 'directory not empty..'
            Directory.CreateDirectory(name);

            SafeFileHandle h = CreateFile(name, GENERIC_WRITE, 0, IntPtr.Zero, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, IntPtr.Zero);

            try
            {
                if (h.IsInvalid)
                {
                    throw new CreationFailedException(Marshal.GetLastWin32Error());
                }

                // Transform the file into a reparse point
                var targetBytes       = Encoding.Unicode.GetBytes(prefix + target);
                var reparseDataBuffer =
                    new REPARSE_DATA_BUFFER
                {
                    ReparseTag           = IO_REPARSE_TAG_MOUNT_POINT,
                    ReparseDataLength    = (ushort)(targetBytes.Length + 12),
                    SubstituteNameOffset = 0,
                    SubstituteNameLength = (ushort)targetBytes.Length,
                    PrintNameOffset      = (ushort)(targetBytes.Length + 2),
                    PrintNameLength      = 0,
                    PathBuffer           = new byte[32768] // This should not be fixed
                };

                Array.Copy(targetBytes, reparseDataBuffer.PathBuffer, targetBytes.Length);

                var inBufferSize = Marshal.SizeOf(reparseDataBuffer);
                var inBuffer     = Marshal.AllocHGlobal(inBufferSize);

                Marshal.StructureToPtr(reparseDataBuffer, inBuffer, false);

                var r = DeviceIoControl(h.DangerousGetHandle(), FSCTL_SET_REPARSE_POINT,
                                        inBuffer, targetBytes.Length + 20, IntPtr.Zero, 0, out int bytesReturned, IntPtr.Zero);

                Marshal.FreeHGlobal(inBuffer);
                if (!r)
                {
                    throw new CreationFailedException(Marshal.GetLastWin32Error());
                }
            }
            finally
            {
                h.Close();
            }
            return;
        }
        /// <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 CreateJunctionPointTo(this PathSpec junctionPoint, PathSpec targetDir, bool overwrite)
        {
            targetDir = targetDir.ToAbsolute();

            if (targetDir.GetPathType() != PathType.Folder)
                throw new IOException("Target path does not exist or is not a directory.");

            if (junctionPoint.GetPathType() == PathType.Folder)
            {
                if (!overwrite)
                    throw new IOException("Directory already exists and overwrite parameter is false.");
            }
            else
            {
                junctionPoint.Create(PathType.Folder);
            }

            using (SafeFileHandle handle = OpenReparsePoint(junctionPoint.ToString(), EFileAccess.GenericWrite))
            {
                byte[] targetDirBytes = Encoding.Unicode.GetBytes(NonInterpretedPathPrefix + Path.GetFullPath(targetDir.ToString()));

                REPARSE_DATA_BUFFER reparseDataBuffer = new REPARSE_DATA_BUFFER();

                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);
                }
            }
        }
Exemplo n.º 28
0
        public static string GetSymlinkTarget(string SymlinkPath)
        {
            if (Environment.OSVersion.Version.Major <= 5)
            {
                throw new InvalidOperationException("Only supported on Vista or later");
            }
            IntPtr hFile = CreateFileNet(
                SymlinkPath,
                EFileAccess.GenericRead,
                EFileShare.Read | EFileShare.Write | EFileShare.Delete,
                IntPtr.Zero,
                ECreationDisposition.OpenExisting,
                EFileAttributes.BackupSemantics | EFileAttributes.OpenReparsePoint,
                IntPtr.Zero
                );

            try
            {
                uint   bufferSize = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
                IntPtr symData    = Marshal.AllocHGlobal((int)bufferSize);
                try
                {
                    uint bytesReturned = 0;
                    try
                    {
                        DeviceIoControlNet(hFile, FSCTL_GET_REPARSE_POINT, IntPtr.Zero, 0, symData, bufferSize, out bytesReturned, IntPtr.Zero);
                    }
                    catch (NotASymlinkException)
                    {
                        throw new NotASymlinkException(string.Format("{0} is not a symlink", SymlinkPath));
                    }
                    REPARSE_DATA_BUFFER symDataHeader = (REPARSE_DATA_BUFFER)Marshal.PtrToStructure(symData, typeof(REPARSE_DATA_BUFFER));
                    if (symDataHeader.ReparseTag == IO_REPARSE_TAG_SYMLINK)
                    {
                        int  subNameLength = ((int)symDataHeader.unnamedUnion.SymbolicLinkReparseBuffer.SubstituteNameLength) / ((int)Marshal.SizeOf(typeof(ushort)));
                        uint subNameOffset = (uint)symDataHeader.unnamedUnion.SymbolicLinkReparseBuffer.SubstituteNameOffset;
                        long pathOffset    = symData.ToInt64();
                        pathOffset += Marshal.OffsetOf(typeof(REPARSE_DATA_BUFFER), "unnamedUnion").ToInt64();
                        pathOffset += Marshal.SizeOf(typeof(SymbolicLinkReparseBufferStructure));
                        pathOffset += subNameOffset;
                        IntPtr pathBuffer = new IntPtr(pathOffset);
                        string subPath    = Marshal.PtrToStringUni(pathBuffer, subNameLength);
                        if (subPath.StartsWith("\\??\\"))
                        {
                            subPath = subPath.Substring(4);
                        }
                        return(subPath);
                    }
                    else
                    {
                        throw new NotASymlinkException(string.Format("{0} is not a symlink", SymlinkPath));
                    }
                }
                finally
                {
                    Marshal.FreeHGlobal(symData);
                }
            }
            finally
            {
                CloseHandleNet(hFile);
            }
        }
        //-----------------------------------------------------------------------
        // Gets the target path for a reparse point
        private static void GetTarget(ref ReparsePoint rp)
        {

            string wPath = rp.Path;
            if (!wPath.StartsWith(@"\\?\")) wPath = @"\\?\" + wPath;

            if (((uint)rp.TagType & (uint)ReparseTagType.IO_REPARSE_TAG_MOUNT_POINT) != (uint)ReparseTagType.IO_REPARSE_TAG_MOUNT_POINT)
            {
                rp.Target = "";
                return;
            }

            try
            {
                // We need a handle to the reparse point to pass to DeviceIocontrol down below.
                // CreateFile is how we get that handle.
                // We want to play nice with others, so we note that we're only reading it, and we want to
                // allow others to be able to access (but not delete) it as well while we have the handle (the
                // GENERIC_READ and FILE_SHARE_READ | FILE_SHARE_WRITE values)
                //
                // Biggest key is the flag FILE_FLAG_OPEN_REPARSE_POINT, which tells CreateFile that we want
                // a handle to the reparse point itself and not to the target of the reparse point
                //
                // CreateFile will return INVALID_HANDLE_VALUE with a last error of 5 - Access Denied
                // if the FILE_FLAG_BACKUP_SEMANTICS is not specified when opening a directory/reparse point
                // It's noted in the directory section of the CreateFile MSDN page
                IntPtr pathHndl = CreateFileW(wPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero,
                OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, IntPtr.Zero);

                if (pathHndl.ToInt32() == INVALID_HANDLE_VALUE)
                {
                    rp.Err = Marshal.GetLastWin32Error();
                    rp.ErrMsg = "Invalid Handle returned by CreateFile";
                    rp.Target = "";
                    return;
                }

                uint lenDataReturned = 0;
                REPARSE_DATA_BUFFER rpDataBuf = new REPARSE_DATA_BUFFER();

                //Allocate a buffer to get the "user defined data" out of the reaprse point.
                //MSDN page on FSCTL_GET_REPARSE_POINT discusses size calculation
                IntPtr pMem = Marshal.AllocHGlobal(Marshal.SizeOf(rpDataBuf) + ReparseHeaderSize);

                //DeviceIocontrol takes a handle to a file/directory/device etc, that's obtained via CreateFile
                // In our case, it's a handle to the directory that's marked a reparse point
                //We pass in the FSCTL_GET_REPARSE_POINT flag to getll DeviceIoControl that we want to get data about a reparse point
                //There is no In buffer. pMem is our out buffer that will hold the returned REPARSE_DATA_BUFFER
                //lenDataReturned is of course how much data was copied into our buffer pMem.
                //We're doing a simple call.. no asyncronous stuff going on, so Overlapped is a null pointer
                if (!DeviceIoControl(pathHndl, FSCTL_GET_REPARSE_POINT, IntPtr.Zero, 0, pMem,
                (uint)Marshal.SizeOf(rpDataBuf) + ReparseHeaderSize, out lenDataReturned, IntPtr.Zero))
                {
                    rp.ErrMsg = "Call to DeviceIoControl failed";
                    rp.Err = Marshal.GetLastWin32Error();
                    rp.Target = "";
                }
                else
                {
                    rpDataBuf = (REPARSE_DATA_BUFFER)Marshal.PtrToStructure(pMem, rpDataBuf.GetType());
                    rp.Target = rpDataBuf.PathBuffer;
                }
                Marshal.FreeHGlobal(pMem);
                CloseHandle(pathHndl);

            }
            catch (Exception e)
            {
                rp.Err = -1;
                rp.ErrMsg = e.Message;
                rp.Target = "";
            }

            return;
        }
Exemplo n.º 30
0
        /// <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="target">The target directory</param>
        /// <exception cref="IOException">Thrown when the junction point could not be created or when
        public void Create(string junctionPoint, string target)
        {
            target = Path.GetFullPath(target);

            Directory.CreateDirectory(junctionPoint);

            using (SafeFileHandle handle = OpenReparsePoint(junctionPoint, EFileAccess.GenericWrite))
            {
                byte[] targetDirBytes = Encoding.Unicode.GetBytes(NonInterpretedPathPrefix + Path.GetFullPath(target));

                var reparseDataBuffer = new REPARSE_DATA_BUFFER();

                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);
                }
            }
        }
Exemplo n.º 31
0
 private static extern bool DeviceIoControl(SafeFileHandle hDevice, UInt32 dwIoControlCode, IntPtr lpInBuffer,
                                            UInt32 nInBufferSize, out REPARSE_DATA_BUFFER rdb, UInt32 nOutBufferSize, out IntPtr lpBytesReturned,
                                            IntPtr lpOverlapped);
Exemplo n.º 32
0
        /// <summary>
        /// Takes a full path to a reparse point and finds the target.
        /// </summary>
        /// <param name="path">Full path of the reparse point</param>
        public ReparsePoint(string path)
        {
            Debug.Assert(!string.IsNullOrEmpty(path) && path.Length > 2 && path[1] == ':' && path[2] == '\\');

            normalisedTarget = "";
            tag = TagType.None;
            bool success;
            int  lastError;
            // Apparently we need to have backup privileges
            IntPtr           token;
            TOKEN_PRIVILEGES tokenPrivileges = new TOKEN_PRIVILEGES();

            tokenPrivileges.Privileges = new LUID_AND_ATTRIBUTES[1];
            success   = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, out token);
            lastError = Marshal.GetLastWin32Error();
            if (success)
            {
                success   = LookupPrivilegeValue(null, SE_BACKUP_NAME, out tokenPrivileges.Privileges[0].Luid);                                 // null for local system
                lastError = Marshal.GetLastWin32Error();
                if (success)
                {
                    tokenPrivileges.PrivilegeCount           = 1;
                    tokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
                    success   = AdjustTokenPrivileges(token, false, ref tokenPrivileges, Marshal.SizeOf(tokenPrivileges), IntPtr.Zero, IntPtr.Zero);
                    lastError = Marshal.GetLastWin32Error();
                }
                CloseHandle(token);
            }

            if (success)
            {
                // Open the file and get its handle
                IntPtr handle = CreateFile(path, FileAccess.Read, FileShare.None, 0, FileMode.Open, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, IntPtr.Zero);
                lastError = Marshal.GetLastWin32Error();
                if (handle.ToInt32() >= 0)
                {
                    REPARSE_DATA_BUFFER buffer = new REPARSE_DATA_BUFFER();
                    // Make up the control code - see CTL_CODE on ntddk.h
                    uint controlCode = (FILE_DEVICE_FILE_SYSTEM << 16) | (FILE_ANY_ACCESS << 14) | (FSCTL_GET_REPARSE_POINT << 2) | METHOD_BUFFERED;
                    uint bytesReturned;
                    success   = DeviceIoControl(handle, controlCode, IntPtr.Zero, 0, out buffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, out bytesReturned, IntPtr.Zero);
                    lastError = Marshal.GetLastWin32Error();
                    if (success)
                    {
                        string subsString  = "";
                        string printString = "";
                        // Note that according to http://wesnerm.blogs.com/net_undocumented/2006/10/symbolic_links_.html
                        // Symbolic links store relative paths, while junctions use absolute paths
                        // however, they can in fact be either, and may or may not have a leading \.
                        Debug.Assert(buffer.ReparseTag == IO_REPARSE_TAG_SYMLINK || buffer.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT,
                                     "Unrecognised reparse tag");                                                               // We only recognise these two
                        if (buffer.ReparseTag == IO_REPARSE_TAG_SYMLINK)
                        {
                            // for some reason symlinks seem to have an extra two characters on the front
                            subsString  = new string(buffer.ReparseTarget, (buffer.SubsNameOffset / 2 + 2), buffer.SubsNameLength / 2);
                            printString = new string(buffer.ReparseTarget, (buffer.PrintNameOffset / 2 + 2), buffer.PrintNameLength / 2);
                            tag         = TagType.SymbolicLink;
                        }
                        else if (buffer.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
                        {
                            // This could be a junction or a mounted drive - a mounted drive starts with "\\??\\Volume"
                            subsString  = new string(buffer.ReparseTarget, buffer.SubsNameOffset / 2, buffer.SubsNameLength / 2);
                            printString = new string(buffer.ReparseTarget, buffer.PrintNameOffset / 2, buffer.PrintNameLength / 2);
                            tag         = subsString.StartsWith(@"\??\Volume") ? TagType.MountPoint : TagType.JunctionPoint;
                        }
                        Debug.Assert(!(string.IsNullOrEmpty(subsString) && string.IsNullOrEmpty(printString)), "Failed to retrieve parse point");
                        // the printstring should give us what we want
                        if (!string.IsNullOrEmpty(printString))
                        {
                            normalisedTarget = printString;
                        }
                        else
                        {
                            // if not we can use the substring with a bit of tweaking
                            normalisedTarget = subsString;
                            Debug.Assert(normalisedTarget.Length > 2, "Target string too short");
                            Debug.Assert(
                                (normalisedTarget.StartsWith(@"\??\") && (normalisedTarget[5] == ':' || normalisedTarget.StartsWith(@"\??\Volume")) ||
                                 (!normalisedTarget.StartsWith(@"\??\") && normalisedTarget[1] != ':')),
                                "Malformed subsString");
                            // Junction points must be absolute
                            Debug.Assert(
                                buffer.ReparseTag == IO_REPARSE_TAG_SYMLINK ||
                                normalisedTarget.StartsWith(@"\??\Volume") ||
                                normalisedTarget[1] == ':',
                                "Relative junction point");
                            if (normalisedTarget.StartsWith(@"\??\"))
                            {
                                normalisedTarget = normalisedTarget.Substring(4);
                            }
                        }
                        actualTarget = normalisedTarget;
                        // Symlinks can be relative.
                        if (buffer.ReparseTag == IO_REPARSE_TAG_SYMLINK && (normalisedTarget.Length < 2 || normalisedTarget[1] != ':'))
                        {
                            // it's relative, we need to tack it onto the path
                            if (normalisedTarget[0] == '\\')
                            {
                                normalisedTarget = normalisedTarget.Substring(1);
                            }
                            if (path.EndsWith(@"\"))
                            {
                                path = path.Substring(0, path.Length - 1);
                            }
                            // Need to take the symlink name off the path
                            normalisedTarget = path.Substring(0, path.LastIndexOf('\\')) + @"\" + normalisedTarget;
                            // Note that if the symlink target path contains any ..s these are not normalised but returned as is.
                        }
                        // Remove any final slash for consistency
                        if (normalisedTarget.EndsWith("\\"))
                        {
                            normalisedTarget = normalisedTarget.Substring(0, normalisedTarget.Length - 1);
                        }
                    }
                    CloseHandle(handle);
                }
                else
                {
                    success = false;
                }
            }
        }
        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(NonInterpretedPathPrefix + Path.GetFullPath(targetDir));

                REPARSE_DATA_BUFFER reparseDataBuffer = new REPARSE_DATA_BUFFER();

                reparseDataBuffer.ReparseTag = IoReparseTagMountPoint;
                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(),
                        FsctlSetReparsePoint,
                        inBuffer,
                        targetDirBytes.Length + 20,
                        IntPtr.Zero,
                        0,
                        out bytesReturned,
                        IntPtr.Zero);

                    if (!result)
                    {
                        ThrowLastWin32Error(Strings.UnableToCreateJunctionPoint);
                    }
                }
                finally
                {
                    Marshal.FreeHGlobal(inBuffer);
                }
            }
        }
Exemplo n.º 34
0
        //-----------------------------------------------------------------------
        // Gets the target path for a reparse point
        private static void GetTarget(ref ReparsePoint rp)
        {
            string wPath = rp.Path;

            if (!wPath.StartsWith(@"\\?\"))
            {
                wPath = @"\\?\" + wPath;
            }

            if (((uint)rp.TagType & (uint)ReparseTagType.IO_REPARSE_TAG_MOUNT_POINT) != (uint)ReparseTagType.IO_REPARSE_TAG_MOUNT_POINT)
            {
                rp.Target = "";
                return;
            }

            try
            {
                // We need a handle to the reparse point to pass to DeviceIocontrol down below.
                // CreateFile is how we get that handle.
                // We want to play nice with others, so we note that we're only reading it, and we want to
                // allow others to be able to access (but not delete) it as well while we have the handle (the
                // GENERIC_READ and FILE_SHARE_READ | FILE_SHARE_WRITE values)
                //
                // Biggest key is the flag FILE_FLAG_OPEN_REPARSE_POINT, which tells CreateFile that we want
                // a handle to the reparse point itself and not to the target of the reparse point
                //
                // CreateFile will return INVALID_HANDLE_VALUE with a last error of 5 - Access Denied
                // if the FILE_FLAG_BACKUP_SEMANTICS is not specified when opening a directory/reparse point
                // It's noted in the directory section of the CreateFile MSDN page
                IntPtr pathHndl = CreateFileW(wPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero,
                                              OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, IntPtr.Zero);

                if (pathHndl.ToInt32() == INVALID_HANDLE_VALUE)
                {
                    rp.Err    = Marshal.GetLastWin32Error();
                    rp.ErrMsg = "Invalid Handle returned by CreateFile";
                    rp.Target = "";
                    return;
                }

                uint lenDataReturned          = 0;
                REPARSE_DATA_BUFFER rpDataBuf = new REPARSE_DATA_BUFFER();

                //Allocate a buffer to get the "user defined data" out of the reaprse point.
                //MSDN page on FSCTL_GET_REPARSE_POINT discusses size calculation
                IntPtr pMem = Marshal.AllocHGlobal(Marshal.SizeOf(rpDataBuf) + ReparseHeaderSize);

                //DeviceIocontrol takes a handle to a file/directory/device etc, that's obtained via CreateFile
                // In our case, it's a handle to the directory that's marked a reparse point
                //We pass in the FSCTL_GET_REPARSE_POINT flag to getll DeviceIoControl that we want to get data about a reparse point
                //There is no In buffer. pMem is our out buffer that will hold the returned REPARSE_DATA_BUFFER
                //lenDataReturned is of course how much data was copied into our buffer pMem.
                //We're doing a simple call.. no asyncronous stuff going on, so Overlapped is a null pointer
                if (!DeviceIoControl(pathHndl, FSCTL_GET_REPARSE_POINT, IntPtr.Zero, 0, pMem,
                                     (uint)Marshal.SizeOf(rpDataBuf) + ReparseHeaderSize, out lenDataReturned, IntPtr.Zero))
                {
                    rp.ErrMsg = "Call to DeviceIoControl failed";
                    rp.Err    = Marshal.GetLastWin32Error();
                    rp.Target = "";
                }
                else
                {
                    rpDataBuf = (REPARSE_DATA_BUFFER)Marshal.PtrToStructure(pMem, rpDataBuf.GetType());
                    rp.Target = rpDataBuf.PathBuffer;
                }
                Marshal.FreeHGlobal(pMem);
                CloseHandle(pathHndl);
            }
            catch (Exception e)
            {
                rp.Err    = -1;
                rp.ErrMsg = e.Message;
                rp.Target = "";
            }

            return;
        }
Exemplo n.º 35
0
        /// <summary>
        /// Takes a full path to a reparse point and finds the target.
        /// </summary>
        /// <param name="path">Full path of the reparse point</param>
        public ReparsePoint(string path)
        {
            //Debug.Assert(!string.IsNullOrEmpty(path) && path.Length > 2 && path[1] == ':' && path[2] == '\\');
            normalisedTarget = string.Empty;
            tag = TagType.None;
            bool success;
            int  lastError;
            // Apparently we need to have backup privileges

            /*IntPtr token;
             * TOKEN_PRIVILEGES tokenPrivileges = new TOKEN_PRIVILEGES();
             * tokenPrivileges.Privileges = new LUID_AND_ATTRIBUTES[1];
             * success = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, out token);
             * lastError = Marshal.GetLastWin32Error();
             * if (success)
             * {
             *  success = LookupPrivilegeValue(null, SE_BACKUP_NAME, out tokenPrivileges.Privileges[0].Luid);			// null for local system
             *  lastError = Marshal.GetLastWin32Error();
             *  if (success)
             *  {
             *      tokenPrivileges.PrivilegeCount = 1;
             *      tokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
             *      success = AdjustTokenPrivileges(token, false, ref tokenPrivileges, Marshal.SizeOf(tokenPrivileges), IntPtr.Zero, IntPtr.Zero);
             *      lastError = Marshal.GetLastWin32Error();
             *  }
             *  CloseHandle(token);
             * }*/

            //if (success)
            {
                // Open the file and get its handle
                using (SafeFileHandle handle = CreateFile(path, EFileAccess.GenericRead, FileShare.ReadWrite | FileShare.Delete, IntPtr.Zero, FileMode.Open, FileAttributesEx.OpenReparsePoint | FileAttributesEx.BackupSemantics, IntPtr.Zero))
                {
                    lastError = Marshal.GetLastWin32Error();
                    if (!handle.IsInvalid)
                    {
                        int    outBufferSize = Marshal.SizeOf(typeof(REPARSE_DATA_BUFFER));
                        IntPtr outBuffer     = Marshal.AllocHGlobal(outBufferSize);

                        //REPARSE_DATA_BUFFER buffer = new REPARSE_DATA_BUFFER();
                        // Make up the control code - see CTL_CODE on ntddk.h
                        //uint controlCode = (FILE_DEVICE_FILE_SYSTEM << 16) | (FILE_ANY_ACCESS << 14) | (FSCTL_GET_REPARSE_POINT << 2) | METHOD_BUFFERED;
                        success = DeviceIoControl(handle.DangerousGetHandle(), EIOControlCode.FsctlGetReparsePoint, IntPtr.Zero, 0, outBuffer, outBufferSize, out _, IntPtr.Zero);
                        //lastError = Marshal.GetLastWin32Error();
                        if (success)
                        {
                            REPARSE_DATA_BUFFER buffer = (REPARSE_DATA_BUFFER)
                                                         Marshal.PtrToStructure(outBuffer, typeof(REPARSE_DATA_BUFFER));

                            string subsString  = string.Empty;
                            string printString = string.Empty;
                            // Note that according to http://wesnerm.blogs.com/net_undocumented/2006/10/symbolic_links_.html
                            // Symbolic links store relative paths, while junctions use absolute paths
                            // however, they can in fact be either, and may or may not have a leading \.

                            /*Debug.Assert(buffer.ReparseTag == IO_REPARSE_TAG_SYMLINK || buffer.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT,
                             *  "Unrecognised reparse tag");						// We only recognise these two
                             */
                            if (buffer.ReparseTag == IO_REPARSE_TAG_SYMLINK)
                            {
                                // for some reason symlinks seem to have an extra two characters on the front
                                subsString  = new string(buffer.ReparseTarget, (buffer.SubsNameOffset / 2 + 2), buffer.SubsNameLength / 2);
                                printString = new string(buffer.ReparseTarget, (buffer.PrintNameOffset / 2 + 2), buffer.PrintNameLength / 2);
                                tag         = TagType.SymbolicLink;
                            }
                            else if (buffer.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
                            {
                                // This could be a junction or a mounted drive - a mounted drive starts with "\\??\\Volume"
                                subsString  = new string(buffer.ReparseTarget, buffer.SubsNameOffset / 2, buffer.SubsNameLength / 2);
                                printString = new string(buffer.ReparseTarget, buffer.PrintNameOffset / 2, buffer.PrintNameLength / 2);
                                tag         = subsString.StartsWith(@"\??\Volume", StringComparison.InvariantCultureIgnoreCase) ? TagType.MountPoint : TagType.JunctionPoint;
                            }
                            //Debug.Assert(!(string.IsNullOrEmpty(subsString) && string.IsNullOrEmpty(printString)), "Failed to retrieve parse point");
                            // the printstring should give us what we want
                            if (printString.Length > 0)
                            {
                                normalisedTarget = printString;
                            }
                            else
                            {
                                // if not we can use the substring with a bit of tweaking
                                normalisedTarget = subsString;

                                /*Debug.Assert(normalisedTarget.Length > 2, "Target string too short");
                                 * Debug.Assert(
                                 *  (normalisedTarget.StartsWith(@"\??\") && (normalisedTarget[5] == ':' || normalisedTarget.StartsWith(@"\??\Volume")) ||
                                 *  (!normalisedTarget.StartsWith(@"\??\") && normalisedTarget[1] != ':')),
                                 *  "Malformed subsString");
                                 * // Junction points must be absolute
                                 * Debug.Assert(
                                 *      buffer.ReparseTag == IO_REPARSE_TAG_SYMLINK ||
                                 *      normalisedTarget.StartsWith(@"\??\Volume") ||
                                 *      normalisedTarget[1] == ':',
                                 *  "Relative junction point");*/
                                if (normalisedTarget.StartsWith(@"\??\", StringComparison.Ordinal))
                                {
                                    normalisedTarget = normalisedTarget.Substring(4);
                                }
                            }
                            actualTarget = normalisedTarget;
                            // Symlinks can be relative.
                            if (buffer.ReparseTag == IO_REPARSE_TAG_SYMLINK && (normalisedTarget.Length < 2 || normalisedTarget[1] != ':'))
                            {
                                // it's relative, we need to tack it onto the path
                                if (normalisedTarget[0] == '\\')
                                {
                                    normalisedTarget = normalisedTarget.Substring(1);
                                }
                                if (path.EndsWith(@"\", StringComparison.Ordinal))
                                {
                                    path = path.Substring(0, path.Length - 1);
                                }
                                // Need to take the symlink name off the path
                                normalisedTarget = path.Substring(0, path.LastIndexOf('\\')) + @"\" + normalisedTarget;
                                // Note that if the symlink target path contains any ..s these are not normalised but returned as is.
                            }
                            // Remove any final slash for consistency
                            if (normalisedTarget.EndsWith("\\", StringComparison.Ordinal))
                            {
                                normalisedTarget = normalisedTarget.Substring(0, normalisedTarget.Length - 1);
                            }
                        }
                        Marshal.FreeHGlobal(outBuffer);
                        handle.Close();
                    }

                    /*else if (lastError == 5)
                     * {
                     *  success = false;
                     * }*/
                    else
                    {
                        throw new Win32Exception(lastError);
                    }
                }
            }
        }