Changeset 2180


Ignore:
Timestamp:
6/18/2010 9:33:10 AM (4 years ago)
Author:
lowjoel
Message:

Better algorithm to find which partitions are on which drives which works on Windows Vista as well. Address #20: Attached Drive Total Wipe

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

Legend:

Unmodified
Added
Removed
  • trunk/eraser/Eraser.Util/NativeMethods/NtDll.cs

    r2155 r2180  
    306306            public ushort MinorVersion; 
    307307        } 
     308 
     309        /// <summary> 
     310        /// Represents a counted Unicode string. 
     311        /// </summary> 
     312        private struct UNICODE_STRING 
     313        { 
     314            /// <summary> 
     315            /// Constructs a UNICODE_STRING object from an existing <see cref="System.String"/> 
     316            /// object. 
     317            /// </summary> 
     318            /// <param name="str">The string to construct from.</param> 
     319            public UNICODE_STRING(string str) 
     320            { 
     321                if (string.IsNullOrEmpty(str)) 
     322                    MaximumLength = Length = 0; 
     323                else 
     324                    MaximumLength = Length = checked((ushort)(str.Length * sizeof(char))); 
     325                Buffer = str; 
     326            } 
     327 
     328            /// <summary> 
     329            /// Allocates an empty string of the given length. 
     330            /// </summary> 
     331            /// <param name="length">The length, in characters, to allocate.</param> 
     332            public UNICODE_STRING(ushort length) 
     333            { 
     334                MaximumLength = checked((ushort)(length * sizeof(char))); 
     335                Length = 0; 
     336                Buffer = new string('\0', length); 
     337            } 
     338             
     339            public override string ToString() 
     340            { 
     341                if (Length / sizeof(char) > Buffer.Length) 
     342                    return Buffer + new string('\0', Length - Buffer.Length / sizeof(char)); 
     343                else 
     344                    return Buffer.Substring(0, Length / sizeof(char)); 
     345            } 
     346 
     347            /// <summary> 
     348            /// Specifies the length, in bytes, of the string pointed to by the Buffer 
     349            /// member, not including the terminating NULL character, if any. 
     350            /// </summary> 
     351            public ushort Length; 
     352 
     353            /// <summary> 
     354            /// Specifies the total size, in bytes, of memory allocated for Buffer. Up to 
     355            /// MaximumLength bytes may be written into the buffer without trampling memory. 
     356            /// </summary> 
     357            public ushort MaximumLength; 
     358 
     359            /// <summary> 
     360            /// Pointer to a wide-character string. 
     361            /// </summary> 
     362            [MarshalAs(UnmanagedType.LPWStr)] 
     363            public string Buffer; 
     364        } 
     365 
     366        /// <summary> 
     367        /// The OBJECT_ATTRIBUTES structure specifies attributes that can be applied to 
     368        /// objects or object handles by routines that create objects and/or return 
     369        /// handles to objects. 
     370        /// </summary> 
     371        private struct OBJECT_ATTRIBUTES : IDisposable 
     372        { 
     373            public OBJECT_ATTRIBUTES(UNICODE_STRING objectName) 
     374                : this() 
     375            { 
     376                Length = (uint)Marshal.SizeOf(this); 
     377                ObjectName = Marshal.AllocHGlobal(Marshal.SizeOf(objectName)); 
     378                Marshal.StructureToPtr(objectName, ObjectName, false); 
     379            } 
     380 
     381            public void Dispose() 
     382            { 
     383                Dispose(true); 
     384                GC.SuppressFinalize(this); 
     385            } 
     386 
     387            public void Dispose(bool disposing) 
     388            { 
     389                if (ObjectName != IntPtr.Zero) 
     390                    Marshal.FreeHGlobal(ObjectName); 
     391            } 
     392             
     393            /// <summary> 
     394            /// The number of bytes of data contained in this structure. 
     395            /// </summary> 
     396            public uint Length; 
     397 
     398            /// <summary> 
     399            /// Optional handle to the root object directory for the path name specified by 
     400            /// the ObjectName member. If RootDirectory is NULL, ObjectName must point 
     401            /// to a fully-qualified object name that includes the full path to the target 
     402            /// object. If RootDirectory is non-NULL, ObjectName specifies an object name 
     403            /// relative to the RootDirectory directory. The RootDirectory handle can refer 
     404            /// to a file system directory or an object directory in the object manager 
     405            /// namespace. 
     406            /// </summary> 
     407            public IntPtr RootDirectory; 
     408 
     409            /// <summary> 
     410            /// Pointer to a Unicode string that contains the name of the object for which 
     411            /// a handle is to be opened. This must either be a fully qualified object name, 
     412            /// or a relative path name to the directory specified by the RootDirectory 
     413            /// member. 
     414            /// </summary> 
     415            public IntPtr ObjectName; 
     416 
     417            /// <summary> 
     418            /// Bitmask of flags that specify object handle attributes. This member can 
     419            /// contain one or more of the flags in the following table. 
     420            /// </summary> 
     421            OBJECT_ATTRIBUTESFlags Attributes; 
     422 
     423            /// <summary> 
     424            /// Specifies a security descriptor (SECURITY_DESCRIPTOR) for the object 
     425            /// when the object is created. If this member is NULL, the object will 
     426            /// receive default security settings. 
     427            /// </summary> 
     428            IntPtr SecurityDescriptor; 
     429 
     430            /// <summary> 
     431            /// Optional quality of service to be applied to the object when it is created. 
     432            /// Used to indicate the security impersonation level and context tracking mode 
     433            /// (dynamic or static). Currently, the InitializeObjectAttributes macro sets 
     434            /// this member to NULL. 
     435            /// </summary> 
     436            IntPtr SecurityQualityOfService; 
     437        } 
     438 
     439        [Flags] 
     440        public enum OBJECT_ATTRIBUTESFlags 
     441        { 
     442            /// <summary> 
     443            /// No flags specified. 
     444            /// </summary> 
     445            None = 0 
     446        } 
     447 
     448        /// <summary> 
     449        /// Opens an existing symbolic link. 
     450        /// </summary> 
     451        /// <param name="LinkHandle">A handle to the newly opened symbolic link object.</param> 
     452        /// <param name="DesiredAccess">An ACCESS_MASK that specifies the requested access 
     453        /// to the directory object. It is typical to use GENERIC_READ so the handle can be 
     454        /// passed to the NtQueryDirectoryObject function.</param> 
     455        /// <param name="ObjectAttributes">The attributes for the directory object.</param> 
     456        /// <returns>The function returns STATUS_SUCCESS or an error status.</returns> 
     457        [DllImport("NtDll.dll")] 
     458        private static extern uint NtOpenSymbolicLinkObject(out IntPtr LinkHandle, 
     459            uint DesiredAccess, ref OBJECT_ATTRIBUTES ObjectAttributes); 
     460 
     461        /// <summary> 
     462        /// Retrieves the target of a symbolic link. 
     463        /// </summary> 
     464        /// <param name="LinkHandle">A handle to the symbolic link object.</param> 
     465        /// <param name="LinkTarget">A pointer to an initialized Unicode string that receives 
     466        /// the target of the symbolic link. The MaximumLength and Buffer members must be 
     467        /// set if the call fails.</param> 
     468        /// <param name="ReturnedLength">A pointer to a variable that receives the length of 
     469        /// the Unicode string returned in the LinkTarget parameter. If the function 
     470        /// returns STATUS_BUFFER_TOO_SMALL, this variable receives the required buffer 
     471        /// size.</param> 
     472        /// <returns>The function returns STATUS_SUCCESS or an error status.</returns> 
     473        [DllImport("NtDll.dll")] 
     474        private static extern uint NtQuerySymbolicLinkObject(IntPtr LinkHandle, 
     475            ref UNICODE_STRING LinkTarget, out uint ReturnedLength); 
     476 
     477        /// <summary> 
     478        /// Queries the provided symbolic link for its target. 
     479        /// </summary> 
     480        /// <param name="path">The path to query.</param> 
     481        /// <returns>The destination of the symbolic link.</returns> 
     482        public static string NtQuerySymbolicLink(string path) 
     483        { 
     484            uint status = 0; 
     485            IntPtr handle = IntPtr.Zero; 
     486            UNICODE_STRING drive = new UNICODE_STRING(path); 
     487            OBJECT_ATTRIBUTES attributes = new OBJECT_ATTRIBUTES(drive); 
     488 
     489            try 
     490            { 
     491                status = NtOpenSymbolicLinkObject(out handle, GENERIC_READ, ref attributes); 
     492                if (status != 0) 
     493                    return null; 
     494            } 
     495            finally 
     496            { 
     497                attributes.Dispose(); 
     498            } 
     499 
     500            UNICODE_STRING target = new UNICODE_STRING(MaxPath); 
     501            uint length = 0; 
     502            for ( ; ; ) 
     503            { 
     504                status = NtQuerySymbolicLinkObject(handle, ref target, out length); 
     505                if (status == 0) 
     506                    break; 
     507                else if (status == 0xC0000023L) //STATUS_BUFFER_TOO_SMALL 
     508                    target = new UNICODE_STRING(target.MaximumLength); 
     509                else 
     510                    return null; 
     511            } 
     512 
     513            return target.ToString(); 
     514        } 
    308515    } 
    309516} 
  • trunk/eraser/Eraser.Util/PhysicalDriveInfo.cs

    r2174 r2180  
    127127            get 
    128128            { 
    129                 //Get all registered DOS Device names. 
    130                 string[] dosDevices = NativeMethods.QueryDosDevices(); 
     129                List<VolumeInfo> result = new List<VolumeInfo>(); 
    131130 
    132                 //Get a list of Win32 device names, and map it to DOS Devices. 
    133                 ComLib.Collections.DictionaryMultiValue<string, string> devices = 
    134                     new ComLib.Collections.DictionaryMultiValue<string, string>(); 
    135                 foreach (string dosDevice in dosDevices) 
     131                //Check every partition index on this drive. 
     132                for (int i = 1; ; ++i) 
    136133                { 
    137                     string win32Device = NativeMethods.QueryDosDevice(dosDevice); 
    138                     if (win32Device != null) 
    139                         devices.Add(win32Device, dosDevice); 
    140                 } 
     134                    string path = string.Format(CultureInfo.InvariantCulture, 
     135                        "\\Device\\Harddisk{0}\\Partition{1}", Index, i); 
     136                    using (SafeFileHandle handle = OpenWin32Device(path)) 
     137                    { 
     138                        if (handle.IsInvalid) 
     139                            break; 
     140                    } 
    141141 
    142                 List<VolumeInfo> result = new List<VolumeInfo>(); 
    143                 foreach (VolumeInfo info in VolumeInfo.Volumes) 
    144                 { 
    145                     if (info.VolumeId.Substring(0, 4) == "\\\\?\\") 
     142                    //This partition index is valid. Check which VolumeInfo this maps to. 
     143                    foreach (VolumeInfo info in VolumeInfo.Volumes) 
    146144                    { 
    147                         string win32Device = NativeMethods.QueryDosDevice( 
    148                             info.VolumeId.Substring(4, info.VolumeId.Length - 5)); 
    149                         foreach (string dosDevice in devices.Get(win32Device)) 
     145                        //Only check local drives 
     146                        if (info.VolumeId.Substring(0, 4) == "\\\\?\\") 
    150147                        { 
    151                             Match match = HarddiskPartitionRegex.Match(dosDevice); 
    152                             if (!match.Success) 
    153                                 continue; 
    154  
    155                             //Check the HardDisk ID: if it is the same as our index, it is our partition. 
    156                             if (Convert.ToInt32(match.Groups[1].Value) == Index) 
     148                            //Check whether the DOS Device maps to the target of the symbolic link 
     149                            if (NativeMethods.NtQuerySymbolicLink(path) == 
     150                                NativeMethods.QueryDosDevice(info.VolumeId.Substring( 
     151                                    4, info.VolumeId.Length - 5))) 
    157152                            { 
    158                                 int partition = Convert.ToInt32(match.Groups[2].Value) - 1; 
    159                                 while (partition >= result.Count) 
    160                                     result.Add(null); 
    161  
    162                                 result[partition] = info; 
     153                                //Yes, this volume belongs to this disk 
     154                                result.Add(info); 
     155                                break; 
    163156                            } 
    164157                        } 
Note: See TracChangeset for help on using the changeset viewer.