Changeset 292


Ignore:
Timestamp:
03/20/08 13:51:49 (7 years ago)
Author:
lowjoel
Message:

Implemented (quite completely, I believe) ADS support
-Cluster tips of ADSes are erased too, this is an improvement over v5
-File times of ADSes are not changed. TODO: Is there a need to?
-Restore the Read only flag after the erase procedure is done
-Implemented the stream equivalent for FileInfo?: StreamInfo?. It lacks many of the features of FileInfo? like ACLs and encryption/decryption, but is complex enough to work as a FileInfo? drop-in in most cases

Location:
branches/eraser6
Files:
1 added
4 edited

Legend:

Unmodified
Added
Removed
  • branches/eraser6/Manager/DirectExecutor.cs

    r283 r292  
    506506                                "erased, because it is a system file", file.FullName), LogLevel.INFORMATION)); 
    507507                        else 
     508                        { 
     509                            foreach (string i in Util.File.GetADSes(file)) 
     510                                files.Add(file.FullName + ':' + i); 
    508511                            files.Add(file.FullName); 
     512                        } 
    509513 
    510514                    foreach (DirectoryInfo subDir in info.GetDirectories()) 
     
    537541        { 
    538542            //Get the file access times 
    539             DateTime lastAccess, lastWrite, created; 
    540             { 
    541                 FileInfo info = new FileInfo(file); 
    542                 lastAccess = info.LastAccessTime; 
    543                 lastWrite = info.LastWriteTime; 
    544                 created = info.CreationTime; 
     543            StreamInfo streamInfo = new StreamInfo(file); 
     544            DateTime lastAccess = DateTime.MinValue, lastWrite = DateTime.MinValue, 
     545                     created = DateTime.MinValue; 
     546            { 
     547                FileInfo info = streamInfo.File; 
     548                if (info != null) 
     549                { 
     550                    lastAccess = info.LastAccessTime; 
     551                    lastWrite = info.LastWriteTime; 
     552                    created = info.CreationTime; 
     553                } 
    545554            } 
    546555 
    547556            //Create the stream, lengthen the file, then tell the erasure method 
    548557            //to erase the tips. 
    549             using (FileStream stream = new FileStream(file, FileMode.Open, 
    550                 FileAccess.Write, FileShare.ReadWrite)) 
     558            using (FileStream stream = streamInfo.Open(FileMode.Open, FileAccess.Write)) 
    551559            { 
    552560                long fileLength = stream.Length; 
     
    570578 
    571579            //Set the file times 
    572             { 
    573                 FileInfo info = new FileInfo(file); 
    574                 info.LastAccessTime = lastAccess; 
    575                 info.LastWriteTime = lastWrite; 
    576                 info.CreationTime = created; 
     580            FileInfo fileInfo = streamInfo.File; 
     581            if (fileInfo != null) 
     582            { 
     583                fileInfo.LastAccessTime = lastAccess; 
     584                fileInfo.LastWriteTime = lastWrite; 
     585                fileInfo.CreationTime = created; 
    577586            } 
    578587        } 
     
    594603                method = ErasureMethodManager.GetInstance(ManagerLibrary.Instance.Settings.DefaultFileErasureMethod); 
    595604 
    596             //Calculate the total amount of data required to finish the wipe. This 
    597             //value is just the total about of data to be erased multiplied by 
    598             //number of passes 
     605            //Calculate the total amount of data required to finish the wipe. 
    599606            dataTotal = method.CalculateEraseDataSize(paths, dataTotal); 
    600607 
     
    613620                task.OnProgressChanged(eventArgs); 
    614621 
    615                 //Make sure the file does not have any attributes which may 
    616                 //affect the erasure process 
    617                 FileInfo info = new FileInfo(paths[i]); 
     622                //Make sure the file does not have any attributes which may affect 
     623                //the erasure process 
     624                bool isReadOnly = false; 
     625                StreamInfo info = new StreamInfo(paths[i]); 
    618626                if ((info.Attributes & FileAttributes.Compressed) != 0 || 
    619627                    (info.Attributes & FileAttributes.Encrypted) != 0 || 
     
    627635 
    628636                //Remove the read-only flag, if it is set. 
    629                 if ((info.Attributes & FileAttributes.ReadOnly) != 0) 
    630                     info.Attributes &= ~FileAttributes.ReadOnly; 
    631  
    632                 //Create the file stream, and call the erasure method 
    633                 //to write to the stream. 
    634                 using (FileStream strm = new FileStream(info.FullName, 
    635                     FileMode.Open, FileAccess.Write, FileShare.None, 
    636                     8, FileOptions.WriteThrough)) 
    637                 { 
    638                     //Set the end of the stream after the wrap-round the cluster size 
    639                     strm.SetLength(GetFileArea(info.FullName)); 
    640  
    641                     //Then erase the file. 
    642                     long itemWritten = 0, 
    643                          itemTotal = method.CalculateEraseDataSize(null, strm.Length); 
    644                     method.Erase(strm, long.MaxValue, 
    645                         PRNGManager.GetInstance(ManagerLibrary.Instance.Settings.ActivePRNG), 
    646                         delegate(long lastWritten, int currentPass) 
     637                if (isReadOnly = info.IsReadOnly) 
     638                    info.IsReadOnly = false; 
     639 
     640                try 
     641                { 
     642                    //Create the file stream, and call the erasure method to write to 
     643                    //the stream. 
     644                    using (FileStream strm = info.Open(FileMode.Open, FileAccess.Write, 
     645                        FileShare.None, FileOptions.WriteThrough)) 
     646                    { 
     647                        //Set the end of the stream after the wrap-round the cluster size 
     648                        strm.SetLength(GetFileArea(paths[i])); 
     649 
     650                        //If the stream is empty, there's nothing to overwrite. Continue 
     651                        //to the next entry 
     652                        if (strm.Length != 0) 
    647653                        { 
    648                             statistics.DataWritten += lastWritten; 
    649                             eventArgs.currentPass = currentPass; 
    650                             eventArgs.currentItemProgress = (int)((itemWritten += lastWritten) * 100 / itemTotal); 
    651                             eventArgs.overallProgress = (int)(statistics.DataWritten * 100 / dataTotal); 
    652  
    653                             if (statistics.Speed != 0) 
    654                                 eventArgs.timeLeft = (int)(dataTotal - statistics.DataWritten) / statistics.Speed; 
    655                             else 
    656                                 eventArgs.timeLeft = -1; 
    657                             task.OnProgressChanged(eventArgs); 
    658  
    659                             lock (currentTask) 
    660                                 if (currentTask.cancelled) 
    661                                     throw new FatalException("The task was cancelled."); 
     654                            //Then erase the file. 
     655                            long itemWritten = 0, 
     656                                 itemTotal = method.CalculateEraseDataSize(null, strm.Length); 
     657                            method.Erase(strm, long.MaxValue, 
     658                                PRNGManager.GetInstance(ManagerLibrary.Instance.Settings.ActivePRNG), 
     659                                delegate(long lastWritten, int currentPass) 
     660                                { 
     661                                    statistics.DataWritten += lastWritten; 
     662                                    eventArgs.currentPass = currentPass; 
     663                                    eventArgs.currentItemProgress = (int) 
     664                                        ((itemWritten += lastWritten) * 100 / itemTotal); 
     665                                    eventArgs.overallProgress = (int) 
     666                                        (statistics.DataWritten * 100 / dataTotal); 
     667 
     668                                    if (statistics.Speed != 0) 
     669                                        eventArgs.timeLeft = (int) 
     670                                            (dataTotal - statistics.DataWritten) / statistics.Speed; 
     671                                    else 
     672                                        eventArgs.timeLeft = -1; 
     673                                    task.OnProgressChanged(eventArgs); 
     674 
     675                                    lock (currentTask) 
     676                                        if (currentTask.cancelled) 
     677                                            throw new FatalException("The task was cancelled."); 
     678                                } 
     679                            ); 
    662680                        } 
    663                     ); 
    664  
    665                     //Set the length of the file to 0. 
    666                     strm.Seek(0, SeekOrigin.Begin); 
    667                     strm.SetLength(0); 
    668                 } 
    669  
    670                 //Remove the file. 
    671                 RemoveFile(info); 
     681 
     682                        //Set the length of the file to 0. 
     683                        strm.Seek(0, SeekOrigin.Begin); 
     684                        strm.SetLength(0); 
     685                    } 
     686 
     687                    //Remove the file. 
     688                    FileInfo fileInfo = info.File; 
     689                    if (fileInfo != null) 
     690                        RemoveFile(fileInfo); 
     691                } 
     692                finally 
     693                { 
     694                    //Re-set the read-only flag 
     695                    info.IsReadOnly = isReadOnly; 
     696                } 
    672697            } 
    673698 
     
    689714        private static long GetFileArea(string filePath) 
    690715        { 
    691             FileInfo info = new FileInfo(filePath); 
     716            StreamInfo info = new StreamInfo(filePath); 
    692717            uint clusterSize = Drive.GetClusterSize(info.Directory.Root.FullName); 
    693718            return (info.Length + (clusterSize - 1)) & ~(clusterSize - 1); 
  • branches/eraser6/Manager/Task.cs

    r283 r292  
    66using System.Runtime.Serialization; 
    77using System.ComponentModel; 
     8using Eraser.Util; 
    89 
    910namespace Eraser.Manager 
     
    104105 
    105106            /// <summary> 
     107            /// Adds ADSes of the given file to the list. 
     108            /// </summary> 
     109            /// <param name="list">The list to add the ADS paths to.</param> 
     110            /// <param name="file">The file to look for ADSes</param> 
     111            protected void GetPathADSes(ref List<string> list, ref long totalSize, string file) 
     112            { 
     113                //Get the ADS names 
     114                List<string> adses = Util.File.GetADSes(new FileInfo(file)); 
     115 
     116                //Then prepend the path. 
     117                foreach (string adsName in adses) 
     118                { 
     119                    string adsPath = file + ':' + adsName; 
     120                    list.Add(adsPath); 
     121                    Util.StreamInfo info = new Util.StreamInfo(adsPath); 
     122                    totalSize += info.Length; 
     123                } 
     124            } 
     125 
     126            /// <summary> 
    106127            /// The path to the file or folder referred to by this object. 
    107128            /// </summary> 
     
    189210            { 
    190211                List<string> result = new List<string>(); 
     212                totalSize = 0; 
     213                GetPathADSes(ref result, ref totalSize, Path); 
     214 
     215                totalSize += new FileInfo(Path).Length; 
    191216                result.Add(Path); 
    192  
    193                 totalSize = new FileInfo(Path).Length; 
    194217                return result; 
    195218            } 
     
    253276                        { 
    254277                            totalSize += file.Length; 
     278                            GetPathADSes(ref result, ref totalSize, file.FullName); 
    255279                            result.Add(file.FullName); 
    256280                        } 
     
    260284                    { 
    261285                        totalSize += file.Length; 
     286                        GetPathADSes(ref result, ref totalSize, file.FullName); 
    262287                        result.Add(file.FullName); 
    263288                    } 
  • branches/eraser6/Util/File.cs

    r291 r292  
    1616    public static class File 
    1717    { 
     18        /// <summary> 
     19        /// Gets the list of ADSes of the given file.  
     20        /// </summary> 
     21        /// <param name="info">The FileInfo object with the file path etc.</param> 
     22        /// <returns>A list containing the names of the ADSes of each file. The 
     23        /// list will be empty if no ADSes exist.</returns> 
     24        public static List<string> GetADSes(FileInfo info) 
     25        { 
     26            List<string> result = new List<string>(); 
     27            using (FileStream stream = info.OpenRead()) 
     28            { 
     29                SafeFileHandle streamHandle = stream.SafeFileHandle; 
     30 
     31                //Allocate the structures 
     32                WIN32_STREAM_ID streamID = new WIN32_STREAM_ID(); 
     33                IntPtr context = IntPtr.Zero; 
     34                uint bytesRead = 0; 
     35 
     36                //Read the header of the WIN32_STREAM_ID 
     37                BackupRead(streamHandle, ref streamID, (uint)Marshal.SizeOf(streamID), 
     38                    ref bytesRead, false, false, ref context); 
     39 
     40                while (bytesRead == Marshal.SizeOf(streamID)) 
     41                { 
     42                    if (streamID.dwStreamId == BACKUP_ALTERNATE_DATA) 
     43                    { 
     44                        //Allocate memory to copy the stream name into, then copy the name 
     45                        IntPtr pName = Marshal.AllocHGlobal((IntPtr)streamID.dwStreamNameSize); 
     46                        uint nameLength = streamID.dwStreamNameSize / sizeof(char); 
     47                        char[] name = new char[nameLength]; 
     48                        BackupRead(streamHandle, pName, streamID.dwStreamNameSize, ref bytesRead, 
     49                            false, false, ref context); 
     50                        Marshal.Copy(pName, name, 0, (int)nameLength); 
     51 
     52                        //Get the name of the stream. The raw value is :NAME:$DATA 
     53                        string streamName = new string(name); 
     54                        result.Add(streamName.Substring(1, streamName.LastIndexOf(':') - 1)); 
     55                    } 
     56 
     57                    //Skip the file contents. Jump to the next header. 
     58                    uint seekLow = 0, seekHigh = 0; 
     59                    BackupSeek(streamHandle, (uint)(streamID.Size & uint.MaxValue), 
     60                        (uint)(streamID.Size >> (sizeof(uint) * 8)), out seekLow, 
     61                        out seekHigh, ref context); 
     62 
     63                    //And try to read the header 
     64                    BackupRead(streamHandle, ref streamID, (uint)Marshal.SizeOf(streamID), 
     65                        ref bytesRead, false, false, ref context); 
     66                } 
     67 
     68                //Free the context 
     69                BackupRead(streamHandle, IntPtr.Zero, 0, ref bytesRead, true, false, ref context); 
     70            } 
     71 
     72            return result; 
     73        } 
     74 
    1875        /// <summary> 
    1976        /// Uses SHGetFileInfo to retrieve the description for the given file, 
     
    411468        private static extern bool SfcIsFileProtected(IntPtr RpcHandle, 
    412469            [MarshalAs(UnmanagedType.LPWStr)]string ProtFileName); 
     470 
     471        /// <summary> 
     472        /// The BackupRead function can be used to back up a file or directory, 
     473        /// including the security information. The function reads data associated 
     474        /// with a specified file or directory into a buffer, which can then be 
     475        /// written to the backup medium using the WriteFile function. 
     476        /// </summary> 
     477        /// <param name="hFile">Handle to the file or directory to be backed up. 
     478        /// To obtain the handle, call the CreateFile function. The SACLs are not 
     479        /// read unless the file handle was created with the ACCESS_SYSTEM_SECURITY 
     480        /// access right. For more information, see File Security and Access Rights. 
     481        ///  
     482        /// The BackupRead function may fail if CreateFile was called with the flag 
     483        /// FILE_FLAG_NO_BUFFERING. In this case, the GetLastError function 
     484        /// returns the value ERROR_INVALID_PARAMETER.</param> 
     485        /// <param name="lpBuffer">Pointer to a buffer that receives the data.</param> 
     486        /// <param name="nNumberOfBytesToRead">Length of the buffer, in bytes. The 
     487        /// buffer size must be greater than the size of a WIN32_STREAM_ID structure.</param> 
     488        /// <param name="lpNumberOfBytesRead">Pointer to a variable that receives 
     489        /// the number of bytes read. 
     490        ///  
     491        /// If the function returns a nonzero value, and the variable pointed to 
     492        /// by lpNumberOfBytesRead is zero, then all the data associated with the 
     493        /// file handle has been read.</param> 
     494        /// <param name="bAbort">Indicates whether you have finished using BackupRead 
     495        /// on the handle. While you are backing up the file, specify this parameter 
     496        /// as FALSE. Once you are done using BackupRead, you must call BackupRead 
     497        /// one more time specifying TRUE for this parameter and passing the appropriate 
     498        /// lpContext. lpContext must be passed when bAbort is TRUE; all other 
     499        /// parameters are ignored.</param> 
     500        /// <param name="bProcessSecurity">Indicates whether the function will 
     501        /// restore the access-control list (ACL) data for the file or directory. 
     502        ///  
     503        /// If bProcessSecurity is TRUE, the ACL data will be backed up.</param> 
     504        /// <param name="lpContext">Pointer to a variable that receives a pointer 
     505        /// to an internal data structure used by BackupRead to maintain context 
     506        /// information during a backup operation. 
     507        ///  
     508        /// You must set the variable pointed to by lpContext to NULL before the 
     509        /// first call to BackupRead for the specified file or directory. The 
     510        /// function allocates memory for the data structure, and then sets the 
     511        /// variable to point to that structure. You must not change lpContext or 
     512        /// the variable that it points to between calls to BackupRead. 
     513        ///  
     514        /// To release the memory used by the data structure, call BackupRead with 
     515        /// the bAbort parameter set to TRUE when the backup operation is complete.</param> 
     516        /// <returns>If the function succeeds, the return value is nonzero. 
     517        ///  
     518        /// If the function fails, the return value is zero, indicating that an 
     519        /// I/O error occurred. To get extended error information, call 
     520        /// Marshal.GetLastWin32Error.</returns> 
     521        [DllImport("Kernel32.dll", SetLastError = true)] 
     522        [return: MarshalAs(UnmanagedType.Bool)] 
     523        private static extern bool BackupRead(SafeFileHandle hFile, 
     524            IntPtr lpBuffer, uint nNumberOfBytesToRead, ref uint lpNumberOfBytesRead, 
     525            [MarshalAs(UnmanagedType.Bool)] bool bAbort, 
     526            [MarshalAs(UnmanagedType.Bool)] bool bProcessSecurity, 
     527            ref IntPtr lpContext); 
     528 
     529         
     530        [DllImport("Kernel32.dll", SetLastError = true)] 
     531        [return: MarshalAs(UnmanagedType.Bool)] 
     532        private static extern bool BackupRead(SafeFileHandle hFile, 
     533            ref WIN32_STREAM_ID lpBuffer, uint nNumberOfBytesToRead, 
     534            ref uint lpNumberOfBytesRead, [MarshalAs(UnmanagedType.Bool)] bool bAbort, 
     535            [MarshalAs(UnmanagedType.Bool)] bool bProcessSecurity, 
     536            ref IntPtr lpContext); 
     537 
     538        /// <summary> 
     539        /// The BackupSeek function seeks forward in a data stream initially 
     540        /// accessed by using the BackupRead or BackupWrite function. 
     541        /// </summary> 
     542        /// <param name="hFile">Handle to the file or directory. This handle is 
     543        /// created by using the CreateFile function.</param> 
     544        /// <param name="dwLowBytesToSeek">Low-order part of the number of bytes 
     545        /// to seek.</param> 
     546        /// <param name="dwHighBytesToSeek">High-order part of the number of bytes 
     547        /// to seek.</param> 
     548        /// <param name="lpdwLowByteSeeked">Pointer to a variable that receives 
     549        /// the low-order bits of the number of bytes the function actually seeks.</param> 
     550        /// <param name="lpdwHighByteSeeked">Pointer to a variable that receives 
     551        /// the high-order bits of the number of bytes the function actually seeks.</param> 
     552        /// <param name="lpContext">Pointer to an internal data structure used by 
     553        /// the function. This structure must be the same structure that was 
     554        /// initialized by the BackupRead function. An application must not touch 
     555        /// the contents of this structure.</param> 
     556        /// <returns>If the function could seek the requested amount, the function 
     557        /// returns a nonzero value. 
     558        ///  
     559        /// If the function could not seek the requested amount, the function  
     560        /// returns zero. To get extended error information, call 
     561        /// Marshal.GetLastWin32Error.</returns> 
     562        [DllImport("Kernel32.dll", SetLastError = true)] 
     563        [return: MarshalAs(UnmanagedType.Bool)] 
     564        private static extern bool BackupSeek(SafeFileHandle hFile, uint dwLowBytesToSeek, 
     565            uint dwHighBytesToSeek, out uint lpdwLowByteSeeked, out uint lpdwHighByteSeeked, 
     566            ref IntPtr lpContext); 
     567 
     568        /// <summary> 
     569        /// The WIN32_STREAM_ID structure contains stream data. 
     570        /// </summary> 
     571        [StructLayout(LayoutKind.Sequential, Pack = 4)] 
     572        private struct WIN32_STREAM_ID 
     573        { 
     574            /// <summary> 
     575            /// Type of data. This member can be one of the BACKUP_* values. 
     576            /// </summary> 
     577            public uint dwStreamId; 
     578 
     579            /// <summary> 
     580            /// Attributes of data to facilitate cross-operating system transfer. 
     581            /// This member can be one or more of the following values. 
     582            /// Value                       Meaning 
     583            /// STREAM_MODIFIED_WHEN_READ   Attribute set if the stream contains 
     584            ///                             data that is modified when read. Allows 
     585            ///                             the backup application to know that 
     586            ///                             verification of data will fail. 
     587            /// STREAM_CONTAINS_SECURITY    Stream contains security data 
     588            ///                             (general attributes). Allows the stream 
     589            ///                             to be ignored on cross-operations restore. 
     590            /// </summary> 
     591            public uint dwStreamAttributes; 
     592 
     593            /// <summary> 
     594            /// Size of data, in bytes. 
     595            /// </summary> 
     596            public long Size; 
     597 
     598            /// <summary> 
     599            /// Length of the name of the alternative data stream, in bytes. 
     600            /// </summary> 
     601            public uint dwStreamNameSize; 
     602        } 
     603 
     604        /// <summary> 
     605        /// Alternative data streams. 
     606        /// </summary> 
     607        public const uint BACKUP_ALTERNATE_DATA = 0x00000004; 
     608 
     609        /// <summary> 
     610        /// Standard data. 
     611        /// </summary> 
     612        public const uint BACKUP_DATA = 0x00000001; 
     613 
     614        /// <summary> 
     615        /// Extended attribute data. 
     616        /// </summary> 
     617        public const uint BACKUP_EA_DATA = 0x00000002; 
     618 
     619        /// <summary> 
     620        /// Hard link information. 
     621        /// </summary> 
     622        public const uint BACKUP_LINK = 0x00000005; 
     623 
     624        /// <summary> 
     625        /// Objects identifiers. 
     626        /// </summary> 
     627        public const uint BACKUP_OBJECT_ID = 0x00000007; 
     628 
     629        /// <summary> 
     630        /// Property data. 
     631        /// </summary> 
     632        public const uint BACKUP_PROPERTY_DATA = 0x00000006; 
     633 
     634        /// <summary> 
     635        /// Reparse points. 
     636        /// </summary> 
     637        public const uint BACKUP_REPARSE_DATA = 0x00000008; 
     638 
     639        /// <summary> 
     640        /// Security descriptor data. 
     641        /// </summary> 
     642        public const uint BACKUP_SECURITY_DATA = 0x00000003; 
     643 
     644        /// <summary> 
     645        /// Sparse file. 
     646        /// </summary> 
     647        public const uint BACKUP_SPARSE_BLOCK = 0x00000009; 
    413648 
    414649        /// <summary> 
  • branches/eraser6/Util/Util.csproj

    r291 r292  
    3939  <ItemGroup> 
    4040    <Compile Include="CryptAPI.cs" /> 
     41    <Compile Include="StreamInfo.cs" /> 
    4142    <Compile Include="KernelAPI.cs" /> 
    4243    <Compile Include="File.cs" /> 
Note: See TracChangeset for help on using the changeset viewer.