Exemple #1
0
        /// <summary>
        /// Return true if the path points to a junction. These are distinct
        /// from symlinks.
        /// </summary>
        /// <returns></returns>
        public bool IsJunction()
        {
            ReparsePoint rep;

            return(ReparsePoint.TryCreate(PurePath.ToString(), out rep) &&
                   rep.Tag == ReparsePoint.TagType.JunctionPoint);
        }
Exemple #2
0
        /// <inheritdoc/>
        public override bool IsSymlink()
        {
            ReparsePoint rep;

            return(ReparsePoint.TryCreate(PurePath.ToString(), out rep) &&
                   rep.Tag == ReparsePoint.TagType.SymbolicLink);
        }
Exemple #3
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>
        /// <param name="link"></param>
        public static bool TryCreate(string path, out ReparsePoint link)
        {
            Debug.Assert(!string.IsNullOrEmpty(path) && path.Length > 2 && path[1] == ':' && path[2] == '\\');

            var tag = TagType.None;
            string normalizedTarget;

            // Apparently we need to have backup privileges
            IntPtr token;
            TOKEN_PRIVILEGES tokenPrivileges = new TOKEN_PRIVILEGES
            {
                Privileges = new LUID_AND_ATTRIBUTES[1]
            };
            bool success = OpenProcessToken(
                GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, out token);

            if (!success)
            {
                link = null;
                return false;
            }

            success = LookupPrivilegeValue(
                null, SE_BACKUP_NAME, out tokenPrivileges.Privileges[0].Luid);
            if (!success)
            {
                link = null;
                return false;
            }

            tokenPrivileges.PrivilegeCount = 1;
            tokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
            success = AdjustTokenPrivileges(token, false, ref tokenPrivileges, Marshal.SizeOf(tokenPrivileges), IntPtr.Zero, IntPtr.Zero);
            CloseHandle(token);

            if (!success)
            {
                link = null;
                return false;
            }
            // 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);
            if (handle.ToInt32() < 0)
            {
                link = null;
                return false;
            }
            REPARSE_DATA_BUFFER buffer;
            // Make up the control code - see CTL_CODE on ntddk.h
            const 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);
            if (!success)
            {
                link = null;
                return false;
            }
            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))
            {
                normalizedTarget = printString;
            }
            else
            {
                // if not we can use the substring with a bit of tweaking
                normalizedTarget = subsString;
                Debug.Assert(normalizedTarget.Length > 2, "Target string too short");
                Debug.Assert(
                    (normalizedTarget.StartsWith(@"\??\") && (normalizedTarget[5] == ':' || normalizedTarget.StartsWith(@"\??\Volume")) ||
                    (!normalizedTarget.StartsWith(@"\??\") && normalizedTarget[1] != ':')),
                    "Malformed subsString");
                // Junction points must be absolute
                Debug.Assert(
                        buffer.ReparseTag == IO_REPARSE_TAG_SYMLINK ||
                        normalizedTarget.StartsWith(@"\??\Volume") ||
                        normalizedTarget[1] == ':' ||
                        normalizedTarget.StartsWith(@"\??\") && normalizedTarget[5] == ':',
                    "Relative junction point");
                if (normalizedTarget.StartsWith(@"\??\"))
                {
                    normalizedTarget = normalizedTarget.Substring(4);
                }
            }

            var target = normalizedTarget;

            // Symlinks can be relative.
            if (buffer.ReparseTag == IO_REPARSE_TAG_SYMLINK && (normalizedTarget.Length < 2 || normalizedTarget[1] != ':'))
            {
                // it's relative, we need to tack it onto the path
                if (normalizedTarget[0] == '\\')
                {
                    normalizedTarget = normalizedTarget.Substring(1);
                }
                if (path.EndsWith(@"\"))
                {
                    path = path.Substring(0, path.Length - 1);
                }
                // Need to take the symlink name off the path
                normalizedTarget = path.Substring(0, path.LastIndexOf('\\')) + @"\" + normalizedTarget;
                // 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 (normalizedTarget.EndsWith("\\"))
            {
                normalizedTarget = normalizedTarget.Substring(0, normalizedTarget.Length-1);
            }
            CloseHandle(handle);

            link = new ReparsePoint
            {
                _normalizedTarget = normalizedTarget,
                Target = target,
                Tag = tag
            };
            return true;
        }
Exemple #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>
        /// <param name="link"></param>
        public static bool TryCreate(string path, out ReparsePoint link)
        {
            Debug.Assert(!string.IsNullOrEmpty(path) && path.Length > 2 && path[1] == ':' && path[2] == '\\');

            var    tag = TagType.None;
            string normalizedTarget;

            // Apparently we need to have backup privileges
            IntPtr           token;
            TOKEN_PRIVILEGES tokenPrivileges = new TOKEN_PRIVILEGES
            {
                Privileges = new LUID_AND_ATTRIBUTES[1]
            };
            bool success = OpenProcessToken(
                GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, out token);

            if (!success)
            {
                link = null;
                return(false);
            }

            success = LookupPrivilegeValue(
                null, SE_BACKUP_NAME, out tokenPrivileges.Privileges[0].Luid);
            if (!success)
            {
                link = null;
                return(false);
            }

            tokenPrivileges.PrivilegeCount           = 1;
            tokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
            success = AdjustTokenPrivileges(token, false, ref tokenPrivileges, Marshal.SizeOf(tokenPrivileges), IntPtr.Zero, IntPtr.Zero);
            CloseHandle(token);

            if (!success)
            {
                link = null;
                return(false);
            }
            // 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);

            if (handle.ToInt32() < 0)
            {
                link = null;
                return(false);
            }
            REPARSE_DATA_BUFFER buffer;
            // Make up the control code - see CTL_CODE on ntddk.h
            const 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);
            if (!success)
            {
                link = null;
                return(false);
            }
            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))
            {
                normalizedTarget = printString;
            }
            else
            {
                // if not we can use the substring with a bit of tweaking
                normalizedTarget = subsString;
                Debug.Assert(normalizedTarget.Length > 2, "Target string too short");
                Debug.Assert(
                    (normalizedTarget.StartsWith(@"\??\") && (normalizedTarget[5] == ':' || normalizedTarget.StartsWith(@"\??\Volume")) ||
                     (!normalizedTarget.StartsWith(@"\??\") && normalizedTarget[1] != ':')),
                    "Malformed subsString");
                // Junction points must be absolute
                Debug.Assert(
                    buffer.ReparseTag == IO_REPARSE_TAG_SYMLINK ||
                    normalizedTarget.StartsWith(@"\??\Volume") ||
                    normalizedTarget[1] == ':' ||
                    normalizedTarget.StartsWith(@"\??\") && normalizedTarget[5] == ':',
                    "Relative junction point");
                if (normalizedTarget.StartsWith(@"\??\"))
                {
                    normalizedTarget = normalizedTarget.Substring(4);
                }
            }

            var target = normalizedTarget;

            // Symlinks can be relative.
            if (buffer.ReparseTag == IO_REPARSE_TAG_SYMLINK && (normalizedTarget.Length < 2 || normalizedTarget[1] != ':'))
            {
                // it's relative, we need to tack it onto the path
                if (normalizedTarget[0] == '\\')
                {
                    normalizedTarget = normalizedTarget.Substring(1);
                }
                if (path.EndsWith(@"\"))
                {
                    path = path.Substring(0, path.Length - 1);
                }
                // Need to take the symlink name off the path
                normalizedTarget = path.Substring(0, path.LastIndexOf('\\')) + @"\" + normalizedTarget;
                // 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 (normalizedTarget.EndsWith("\\"))
            {
                normalizedTarget = normalizedTarget.Substring(0, normalizedTarget.Length - 1);
            }
            CloseHandle(handle);

            link = new ReparsePoint
            {
                _normalizedTarget = normalizedTarget,
                Target            = target,
                Tag = tag
            };
            return(true);
        }