Changeset 2549


Ignore:
Timestamp:
3/19/2012 8:06:33 AM (2 years ago)
Author:
lowjoel
Message:

Forward-port from Eraser 6.0: We need to recursively dereference all reparse points before we call GetVolumeNameForVolumeMountPoint?. This prevents a situation where we we get an invalid parameter error from GetVolumeNameForVolumeMountPoint? when we hand it a reparse point but it is not a volume reparse point.

Fixes bug in https://eraser.heidi.ie/forum/viewtopic.php?f=2&t=8684.

Location:
trunk/eraser/Eraser.Util
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/eraser/Eraser.Util/ExtensionMethods/PathUtil.cs

    r2516 r2549  
    2727using System.Drawing; 
    2828using System.Windows.Forms; 
     29using System.Runtime.InteropServices; 
     30using Microsoft.Win32.SafeHandles; 
    2931 
    3032namespace Eraser.Util.ExtensionMethods 
     
    162164            return GetCompactPath(longPath, newWidth, control.Font); 
    163165        } 
     166 
     167        /// <summary> 
     168        /// Resolves the reparse point pointed to by the path. 
     169        /// </summary> 
     170        /// <param name="path">The path to the reparse point.</param> 
     171        /// <returns>The NT Namespace Name of the reparse point.</returns> 
     172        public static string ResolveReparsePoint(string path) 
     173        { 
     174            if (string.IsNullOrEmpty(path)) 
     175                throw new ArgumentException(path); 
     176 
     177            //If the path is a directory, remove the trailing \ 
     178            if (Directory.Exists(path) && path[path.Length - 1] == '\\') 
     179                path = path.Remove(path.Length - 1); 
     180 
     181            using (SafeFileHandle handle = NativeMethods.CreateFile(path, 
     182                NativeMethods.GENERIC_READ, 
     183                NativeMethods.FILE_SHARE_READ | NativeMethods.FILE_SHARE_WRITE, 
     184                IntPtr.Zero, NativeMethods.OPEN_EXISTING, 
     185                NativeMethods.FILE_FLAG_OPEN_REPARSE_POINT | 
     186                NativeMethods.FILE_FLAG_BACKUP_SEMANTICS, IntPtr.Zero)) 
     187            { 
     188                if (handle.IsInvalid) 
     189                    throw new System.IO.IOException(string.Format("Cannot open handle to {0}", path)); 
     190 
     191                int bufferSize = Marshal.SizeOf(typeof(NativeMethods.REPARSE_DATA_BUFFER)) + 260 * sizeof(char); 
     192                IntPtr buffer = Marshal.AllocHGlobal(bufferSize); 
     193 
     194                //Get all the information about the reparse point. 
     195                try 
     196                { 
     197                    uint returnedBytes = 0; 
     198                    while (!NativeMethods.DeviceIoControl(handle, NativeMethods.FSCTL_GET_REPARSE_POINT, 
     199                        IntPtr.Zero, 0, buffer, (uint)bufferSize, out returnedBytes, IntPtr.Zero)) 
     200                    { 
     201                        if (Marshal.GetLastWin32Error() == 122) //ERROR_INSUFFICIENT_BUFFER 
     202                        { 
     203                            bufferSize *= 2; 
     204                            Marshal.FreeHGlobal(buffer); 
     205                            buffer = Marshal.AllocHGlobal(bufferSize); 
     206                        } 
     207                        else 
     208                        { 
     209                            throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error()); 
     210                        } 
     211                    } 
     212 
     213                    string result = ResolveReparsePoint(buffer, path); 
     214 
     215                    //Is it a directory? If it is, we need to add a trailing \ 
     216                    if (Directory.Exists(path)) 
     217                        result += '\\'; 
     218                    return result; 
     219                } 
     220                finally 
     221                { 
     222                    Marshal.FreeHGlobal(buffer); 
     223                } 
     224            } 
     225        } 
     226 
     227        private static string ResolveReparsePoint(IntPtr ptr, string path) 
     228        { 
     229            NativeMethods.REPARSE_DATA_BUFFER buffer = (NativeMethods.REPARSE_DATA_BUFFER) 
     230                Marshal.PtrToStructure(ptr, typeof(NativeMethods.REPARSE_DATA_BUFFER)); 
     231 
     232            //Check that this Reparse Point has a Microsoft Tag 
     233            if (((uint)buffer.ReparseTag & (1 << 31)) == 0) 
     234            { 
     235                //We can only handle Microsoft's reparse tags. 
     236                throw new ArgumentException("Unknown Reparse point type."); 
     237            } 
     238 
     239            //Then handle the tags 
     240            switch (buffer.ReparseTag) 
     241            { 
     242                case NativeMethods.REPARSE_DATA_TAG.IO_REPARSE_TAG_MOUNT_POINT: 
     243                    return ResolveReparsePointJunction((IntPtr)(ptr.ToInt64() + Marshal.SizeOf(buffer))); 
     244 
     245                case NativeMethods.REPARSE_DATA_TAG.IO_REPARSE_TAG_SYMLINK: 
     246                    return ResolveReparsePointSymlink((IntPtr)(ptr.ToInt64() + Marshal.SizeOf(buffer)), 
     247                        path); 
     248 
     249                default: 
     250                    throw new ArgumentException("Unsupported Reparse point type."); 
     251            } 
     252        } 
     253 
     254        private static string ResolveReparsePointJunction(IntPtr ptr) 
     255        { 
     256            NativeMethods.REPARSE_DATA_BUFFER.MountPointReparseBuffer buffer = 
     257                (NativeMethods.REPARSE_DATA_BUFFER.MountPointReparseBuffer) 
     258                Marshal.PtrToStructure(ptr, 
     259                    typeof(NativeMethods.REPARSE_DATA_BUFFER.MountPointReparseBuffer)); 
     260 
     261            //Get the substitute and print names from the buffer. 
     262            string substituteName; 
     263            string printName; 
     264            unsafe 
     265            { 
     266                char* path = (char*)(((byte*)ptr.ToInt64()) + Marshal.SizeOf(buffer)); 
     267                printName = new string(path + (buffer.PrintNameOffset / sizeof(char)), 0, 
     268                    buffer.PrintNameLength / sizeof(char)); 
     269                substituteName = new string(path + (buffer.SubstituteNameOffset / sizeof(char)), 0, 
     270                    buffer.SubstituteNameLength / sizeof(char)); 
     271            } 
     272 
     273            return substituteName; 
     274        } 
     275 
     276        private static string ResolveReparsePointSymlink(IntPtr ptr, string path) 
     277        { 
     278            NativeMethods.REPARSE_DATA_BUFFER.SymbolicLinkReparseBuffer buffer = 
     279                (NativeMethods.REPARSE_DATA_BUFFER.SymbolicLinkReparseBuffer) 
     280                Marshal.PtrToStructure(ptr, 
     281                    typeof(NativeMethods.REPARSE_DATA_BUFFER.SymbolicLinkReparseBuffer)); 
     282 
     283            //Get the substitute and print names from the buffer. 
     284            string substituteName; 
     285            string printName; 
     286            unsafe 
     287            { 
     288                char* pathBuffer = (char*)(((byte*)ptr.ToInt64()) + Marshal.SizeOf(buffer)); 
     289                printName = new string(pathBuffer + (buffer.PrintNameOffset / sizeof(char)), 0, 
     290                    buffer.PrintNameLength / sizeof(char)); 
     291                substituteName = new string(pathBuffer + (buffer.SubstituteNameOffset / sizeof(char)), 0, 
     292                    buffer.SubstituteNameLength / sizeof(char)); 
     293            } 
     294 
     295            if ((buffer.Flags & NativeMethods.REPARSE_DATA_BUFFER.SymbolicLinkFlags.SYMLINK_FLAG_RELATIVE) != 0) 
     296            { 
     297                return Path.Combine(Path.GetDirectoryName(path), substituteName); 
     298            } 
     299 
     300            return substituteName; 
     301        } 
    164302    } 
    165303} 
  • trunk/eraser/Eraser.Util/NativeMethods/Kernel.cs

    r2516 r2549  
    652652        [DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] 
    653653        [return: MarshalAs(UnmanagedType.Bool)] 
    654         public static extern bool DeviceIoControl(SafeFileHandle hDevice, 
     654        public extern static bool DeviceIoControl(SafeFileHandle hDevice, 
    655655            uint dwIoControlCode, IntPtr lpInBuffer, uint nInBufferSize, 
    656656            out NTFS_VOLUME_DATA_BUFFER lpOutBuffer, uint nOutBufferSize, 
     
    764764            /// </summary> 
    765765            public long ExtentLength; 
     766        } 
     767 
     768        public const uint FSCTL_GET_REPARSE_POINT = (9 << 16) | (42 << 2); 
     769 
     770        /// <summary> 
     771        /// The REPARSE_DATA_BUFFER structure contains reparse point data for a 
     772        /// Microsoft reparse point. (Third-party reparse point owners must use 
     773        /// the REPARSE_GUID_DATA_BUFFER structure instead.)  
     774        /// </summary> 
     775        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 
     776        public struct REPARSE_DATA_BUFFER 
     777        { 
     778            /// <summary> 
     779            /// Contains the reparse point information for a Symbolic Link. 
     780            /// </summary> 
     781            /// <remarks>The PathBuffer member found at the end of the C structure 
     782            /// declaration is appended at the end of this structure.</remarks> 
     783            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 
     784            public struct SymbolicLinkReparseBuffer 
     785            { 
     786                /// <summary> 
     787                /// Offset, in bytes, of the substitute name string in the PathBuffer 
     788                /// array. Note that this offset must be divided by sizeof(char) to 
     789                /// get the array index. 
     790                /// </summary> 
     791                public ushort SubstituteNameOffset; 
     792 
     793                /// <summary> 
     794                /// Length, in bytes, of the substitute name string. If this string is 
     795                /// NULL-terminated, SubstituteNameLength does not include space for 
     796                /// the UNICODE_NULL character. 
     797                /// </summary> 
     798                public ushort SubstituteNameLength; 
     799 
     800                /// <summary> 
     801                /// Offset, in bytes, of the print name string in the PathBuffer array. 
     802                /// Note that this offset must be divided by sizeof(char) to get the 
     803                /// array index. 
     804                /// </summary> 
     805                public ushort PrintNameOffset; 
     806 
     807                /// <summary> 
     808                /// Length, in bytes, of the print name string. If this string is 
     809                /// NULL-terminated, PrintNameLength does not include space for the 
     810                /// UNICODE_NULL character. 
     811                /// </summary> 
     812                public ushort PrintNameLength; 
     813 
     814                /// <summary> 
     815                /// Used to indicate if the given symbolic link is an absolute or relative 
     816                /// symbolic link. If Flags contains SYMLINK_FLAG_RELATIVE, the symbolic 
     817                /// link contained in the PathBuffer array (at offset SubstitueNameOffset) 
     818                /// is processed as a relative symbolic link; otherwise, it is processed 
     819                /// as an absolute symbolic link. 
     820                /// </summary> 
     821                public SymbolicLinkFlags Flags; 
     822            } 
     823 
     824            /// <summary> 
     825            /// Contains the reparse point information for a Directory Junction. 
     826            /// </summary> 
     827            /// <remarks>The PathBuffer member found at the end of the C structure 
     828            /// declaration is appended at the end of this structure.</remarks> 
     829            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 
     830            public struct MountPointReparseBuffer 
     831            { 
     832                /// <summary> 
     833                /// Offset, in bytes, of the substitute name string in the PathBuffer 
     834                /// array. Note that this offset must be divided by sizeof(char) to 
     835                /// get the array index. 
     836                /// </summary> 
     837                public ushort SubstituteNameOffset; 
     838 
     839                /// <summary> 
     840                /// Length, in bytes, of the substitute name string. If this string is 
     841                /// NULL-terminated, SubstituteNameLength does not include space for 
     842                /// the UNICODE_NULL character. 
     843                /// </summary> 
     844                public ushort SubstituteNameLength; 
     845 
     846                /// <summary> 
     847                /// Offset, in bytes, of the print name string in the PathBuffer array. 
     848                /// Note that this offset must be divided by sizeof(char) to get the 
     849                /// array index. 
     850                /// </summary> 
     851                public ushort PrintNameOffset; 
     852 
     853                /// <summary> 
     854                /// Length, in bytes, of the print name string. If this string is 
     855                /// NULL-terminated, PrintNameLength does not include space for the 
     856                /// UNICODE_NULL character. 
     857                /// </summary> 
     858                public ushort PrintNameLength; 
     859            } 
     860 
     861            [Flags] 
     862            public enum SymbolicLinkFlags 
     863            { 
     864                /// <summary> 
     865                /// <see cref="SymbolicLinkReparseBuffer.Flags"/> 
     866                /// </summary> 
     867                SYMLINK_FLAG_RELATIVE = 0x00000001 
     868            } 
     869 
     870            /// <summary> 
     871            /// Reparse point tag. Must be a Microsoft reparse point tag. (See the following Remarks section.) 
     872            /// </summary> 
     873            public REPARSE_DATA_TAG ReparseTag; 
     874 
     875            /// <summary> 
     876            /// Size, in bytes, of the reparse data in the DataBuffer member. 
     877            /// </summary> 
     878            public ushort ReparseDataLength; 
     879            ushort Reserved; 
     880        } 
     881 
     882        /// <summary> 
     883        /// See http://msdn.microsoft.com/en-us/library/windows/desktop/aa365511%28v=vs.85%29.aspx. 
     884        /// </summary> 
     885        public enum REPARSE_DATA_TAG : uint 
     886        { 
     887            IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003, 
     888            IO_REPARSE_TAG_HSM = 0xC0000004, 
     889            IO_REPARSE_TAG_HSM2 = 0x80000006, 
     890            IO_REPARSE_TAG_SIS = 0x80000007, 
     891            IO_REPARSE_TAG_WIM = 0x80000008, 
     892            IO_REPARSE_TAG_CSV = 0x80000009, 
     893            IO_REPARSE_TAG_DFS = 0x8000000A, 
     894            IO_REPARSE_TAG_SYMLINK = 0xA000000C, 
     895            IO_REPARSE_TAG_DFSR = 0x80000012 
    766896        } 
    767897 
  • trunk/eraser/Eraser.Util/VolumeInfo.cs

    r2516 r2549  
    304304                else 
    305305                { 
     306                    //If this is a mountpoint, resolve it before calling 
     307                    //GetVolumeNameForVolumeMountPoint since it will return an error if 
     308                    //the path given is a reparse point, but not a volume reparse point. 
     309                    while ((new DirectoryInfo(currentDir).Attributes & FileAttributes.ReparsePoint) != 0) 
     310                    { 
     311                        currentDir = ExtensionMethods.PathUtil.ResolveReparsePoint(currentDir); 
     312 
     313                        //Strip the NT namespace bit 
     314                        if (currentDir.StartsWith("\\??\\Volume")) 
     315                            throw new ArgumentException(S._("The path provided includes a reparse" + 
     316                                "point which references another volume.")); 
     317                        else 
     318                            currentDir = currentDir.Substring(4); 
     319                        mountpointDir = new DirectoryInfo(currentDir); 
     320                    } 
     321 
    306322                    if (!NativeMethods.GetVolumeNameForVolumeMountPoint(currentDir, volumeID, 50)) 
    307323                    { 
Note: See TracChangeset for help on using the changeset viewer.