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);
示例#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("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);
                }
            }
        }
示例#3
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);
                }
            }
        }
示例#4
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;
                }
            }
        }
示例#5
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);
                }
            }
        }
        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);
                }
            }
        }
        //-----------------------------------------------------------------------
        // 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;
        }
        /// <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);
                }
            }
        }