Example #1
0
        /// <summary>
        /// Constructs a new USN helper instance
        /// </summary>
        /// <param name="volumeRoot">The root volume where the USN lookup is performed</param>
        internal USNJournal(string volumeRoot)
        {
            if (Utility.Utility.IsClientLinux)
            {
                throw new Interface.UserInformationException(Strings.USNHelper.LinuxNotSupportedError, "UsnOnLinuxNotSupported");
            }

            m_volume = Utility.Utility.AppendDirSeparator(volumeRoot);

            try
            {
                var device = GetDeviceNameFromPath(m_volume);

                m_volumeHandle = Win32USN.CreateFile(device, Win32USN.FileAccess.GenericRead,
                                                     Win32USN.FileShare.ReadWrite, IntPtr.Zero, Win32USN.CreationDisposition.OpenExisting,
                                                     Win32USN.FileAttributes.BackupSemantics, IntPtr.Zero);

                if (m_volumeHandle == null || m_volumeHandle.IsInvalid)
                {
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }

                Win32USN.ControlWithOutput(m_volumeHandle, Win32USN.FsCtl.QueryUSNJournal, ref m_journal);

                m_volumeRootRefNumber = GetFileRefNumber(volumeRoot);
            }
            catch
            {
                Dispose();
                throw;
            }
        }
Example #2
0
        /// <summary>
        /// Constructs a new USN helper instance
        /// </summary>
        /// <param name="path">The path to the folder to perform USN services</param>
        /// <param name="volumeRoot">The root volume where the USN lookup is performed</param>
        internal USNHelper(string path, string volumeRoot)
        {
            if (Utility.Utility.IsClientLinux)
            {
                throw new Duplicati.Library.Interface.UserInformationException(Strings.USNHelper.LinuxNotSupportedError, "UsnOnLinuxNotSupported");
            }

            if (!System.IO.Path.IsPathRooted(path))
            {
                throw new Exception(string.Format("Path {0} is not rooted", path));
            }

            m_path = Utility.Utility.AppendDirSeparator(path);

            try
            {
                string devicename = @"\\.\" + System.IO.Path.GetPathRoot(path).TrimEnd('\\');
                if (volumeRoot != null)
                {
                    volumeRoot = volumeRoot.TrimEnd('\\');
                }

                m_volumeHandle = Win32USN.CreateFile(volumeRoot == null ? devicename : volumeRoot, Win32USN.EFileAccess.GenericRead, Win32USN.EFileShare.ReadWrite, IntPtr.Zero, Win32USN.ECreationDisposition.OpenExisting, Win32USN.EFileAttributes.BackupSemantics, IntPtr.Zero);
                if (m_volumeHandle == null || m_volumeHandle.IsInvalid)
                {
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }

                uint bytesReturned = 0;
                if (!Win32USN.DeviceIoControl(m_volumeHandle, Win32USN.EIOControlCode.FsctlQueryUsnJournal, null, 0, out m_journal, (uint)Marshal.SizeOf(typeof(Win32USN.USN_JOURNAL_DATA)), ref bytesReturned, IntPtr.Zero))
                {
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }

                Win32USN.BY_HANDLE_FILE_INFORMATION fileInfo;
                using (SafeFileHandle driveHandle = Win32USN.CreateFile(System.IO.Path.GetPathRoot(path), Win32USN.EFileAccess.GenericRead, Win32USN.EFileShare.ReadWrite, IntPtr.Zero, Win32USN.ECreationDisposition.OpenExisting, Win32USN.EFileAttributes.BackupSemantics, IntPtr.Zero))
                    if (!Win32USN.GetFileInformationByHandle(driveHandle, out fileInfo))
                    {
                        throw new Win32Exception(Marshal.GetLastWin32Error());
                    }

                m_volumeRootFileNameReferenceNumber = ((ulong)fileInfo.FileIndexHigh << 32) | ((ulong)fileInfo.FileIndexLow);
            }
            catch
            {
                if (m_volumeHandle != null)
                {
                    m_volumeHandle.Dispose();
                    m_volumeHandle = null;
                }

                throw;
            }

            if (this.FileSystemEntries.Count == 0)
            {
                throw new Exception(Strings.USNHelper.SafeGuardError);
            }
        }
Example #3
0
        /// <summary>
        /// Unused internal function that can be used to read all USN records
        /// </summary>
        /// <param name="lastUsn">The USN number to start from</param>
        private void GetChangedItems(long lastUsn)
        {
            const int ALLOCATED_MEMORY = 64 * 1024;

            IntPtr allocatedMemory = IntPtr.Zero;
            List <KeyValuePair <string, Win32USN.USN_RECORD> > records = new List <KeyValuePair <string, Win32USN.USN_RECORD> >();

            try
            {
                uint bytesRead = 0;
                bool more      = true;
                allocatedMemory = Marshal.AllocHGlobal(ALLOCATED_MEMORY);

                Win32USN.READ_USN_JOURNAL_DATA startParams = new Win32USN.READ_USN_JOURNAL_DATA();
                startParams.UsnJournalID      = m_journal.UsnJournalID;
                startParams.StartUsn          = lastUsn;
                startParams.ReasonMask        = Win32USN.USNReason.USN_REASON_ANY;
                startParams.ReturnOnlyOnClose = 0;
                startParams.Timeout           = 0;
                startParams.BytesToWaitFor    = 0;

                while (more)
                {
                    if (!Win32USN.DeviceIoControl(m_volumeHandle, Win32USN.EIOControlCode.FsctlReadUsnJournal,
                                                  startParams, (uint)Marshal.SizeOf(typeof(Win32USN.READ_USN_JOURNAL_DATA)),
                                                  allocatedMemory, ALLOCATED_MEMORY,
                                                  ref bytesRead, IntPtr.Zero))
                    {
                        int errorCode = Marshal.GetLastWin32Error();

                        //If we get no error or EOF the enumeration is completed
                        if (errorCode == Win32USN.ERROR_HANDLE_EOF || errorCode == Win32USN.ERROR_SUCCESS)
                        {
                            break;
                        }
                        else
                        {
                            throw new Win32Exception(errorCode);
                        }
                    }

                    startParams.StartUsn = ExtractUsnEntries(bytesRead, allocatedMemory, records, out more);
                }

                //Records now contains all Usn entries
            }
            finally
            {
                if (allocatedMemory != IntPtr.Zero)
                {
                    Marshal.FreeHGlobal(allocatedMemory);
                    allocatedMemory = IntPtr.Zero;
                }
            }
        }
Example #4
0
        /// <summary>
        /// Returns a list of all files and folders changed since the USN
        /// </summary>
        /// <param name="startUsn">The USN number to start the list from, set to zero to get all</param>
        /// <returns>A list of files and folders changed since the USN</returns>
        private List <KeyValuePair <string, Win32USN.USN_RECORD> > BuildUSNTable(long startUsn)
        {
            const int ALLOCATED_MEMORY = 64 * 1024;

            IntPtr allocatedMemory = IntPtr.Zero;
            List <KeyValuePair <string, Win32USN.USN_RECORD> > records = new List <KeyValuePair <string, Win32USN.USN_RECORD> >();

            try
            {
                uint bytesRead = 0;
                bool more      = true;
                allocatedMemory = Marshal.AllocHGlobal(ALLOCATED_MEMORY);

                Win32USN.MFT_ENUM_DATA startParams = new Win32USN.MFT_ENUM_DATA();
                startParams.StartFileReferenceNumber = 0;
                startParams.LowUsn  = Math.Max(startUsn, m_journal.LowestValidUsn);
                startParams.HighUsn = m_journal.NextUsn;

                while (more)
                {
                    if (!Win32USN.DeviceIoControl(m_volumeHandle, Win32USN.EIOControlCode.FsctlEnumUsnData,
                                                  ref startParams, (uint)Marshal.SizeOf(typeof(Win32USN.MFT_ENUM_DATA)),
                                                  allocatedMemory, ALLOCATED_MEMORY,
                                                  ref bytesRead, IntPtr.Zero))
                    {
                        int errorCode = Marshal.GetLastWin32Error();

                        //If we get no error or EOF the enumeration is completed
                        if (errorCode == Win32USN.ERROR_HANDLE_EOF || errorCode == Win32USN.ERROR_SUCCESS)
                        {
                            break;
                        }
                        else
                        {
                            throw new Win32Exception(errorCode);
                        }
                    }

                    startParams.StartFileReferenceNumber = (ulong)ExtractUsnEntries(bytesRead, allocatedMemory, records, out more);
                }

                return(ParseRecordList(records));
            }
            finally
            {
                if (allocatedMemory != IntPtr.Zero)
                {
                    Marshal.FreeHGlobal(allocatedMemory);
                    allocatedMemory = IntPtr.Zero;
                }
            }
        }
Example #5
0
        /// <summary>
        /// Get NTFS file reference number (FRN) from path
        /// </summary>
        /// <param name="filePath">Input path</param>
        /// <returns>NTFS file reference number</returns>
        private static ulong GetFileRefNumber(string filePath)
        {
            Win32USN.BY_HANDLE_FILE_INFORMATION fileInfo;
            using (var driveHandle = Win32USN.CreateFile(filePath, Win32USN.FileAccess.GenericRead,
                                                         Win32USN.FileShare.ReadWrite, IntPtr.Zero, Win32USN.CreationDisposition.OpenExisting,
                                                         Win32USN.FileAttributes.BackupSemantics, IntPtr.Zero))
            {
                if (!Win32USN.GetFileInformationByHandle(driveHandle, out fileInfo))
                {
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }
            }

            return(((ulong)fileInfo.FileIndexHigh << 32) | fileInfo.FileIndexLow);
        }
Example #6
0
        /// <summary>
        /// Retrieves a USN_RECORD_V2 by file reference number (FRN, not USN!)
        /// </summary>
        /// <param name="frn">File reference number</param>
        /// <returns>Returned entry if successful; null otherwise</returns>
        private Record GetRecordByFileRef(ulong frn)
        {
            var enumData = new Win32USN.MFT_ENUM_DATA
            {
                StartFileReferenceNumber = frn,
                LowUsn  = 0,
                HighUsn = m_journal.NextUsn
            };

            var bufferSize = 512;

            byte[] entryData;
            while (!Win32USN.ControlWithInput(m_volumeHandle, Win32USN.FsCtl.EnumUSNData,
                                              ref enumData, bufferSize, out entryData))
            {
                var e = Marshal.GetLastWin32Error();
                if (e != Win32USN.ERROR_INSUFFICIENT_BUFFER)
                {
                    return(null);
                }

                // retry, increasing buffer size
                bufferSize *= 2;
            }

            // not really a foreach: we only check the first record
            foreach (var rec in EnumerateRecords(entryData))
            {
                if (rec.UsnRecord.FileReferenceNumber == frn)
                {
                    return(rec);
                }
                break;
            }

            return(null);
        }
Example #7
0
        /// <summary>
        /// Returns collection of USN records, starting at startUSN
        /// </summary>
        /// <param name="startUsn">The USN number to start the list from, set to zero to get all</param>
        /// <returns>A list of files and folders changed since the USN</returns>
        private ICollection <Record> GetRawRecords(long startUsn)
        {
            var records = new List <Record>();

            var readData = new Win32USN.READ_USN_JOURNAL_DATA_V0
            {
                StartUsn   = Math.Max(startUsn, m_journal.LowestValidUsn),
                ReasonMask = Win32USN.USNReason.USN_REASON_BASIC_INFO_CHANGE |
                             Win32USN.USNReason.USN_REASON_DATA_EXTEND |
                             Win32USN.USNReason.USN_REASON_DATA_OVERWRITE |
                             Win32USN.USNReason.USN_REASON_DATA_TRUNCATION |
                             Win32USN.USNReason.USN_REASON_EA_CHANGE |
                             Win32USN.USNReason.USN_REASON_FILE_CREATE |
                             Win32USN.USNReason.USN_REASON_FILE_DELETE |
                             Win32USN.USNReason.USN_REASON_HARD_LINK_CHANGE |
                             Win32USN.USNReason.USN_REASON_NAMED_DATA_EXTEND |
                             Win32USN.USNReason.USN_REASON_NAMED_DATA_OVERWRITE |
                             Win32USN.USNReason.USN_REASON_NAMED_DATA_TRUNCATION |
                             Win32USN.USNReason.USN_REASON_RENAME_NEW_NAME |
                             Win32USN.USNReason.USN_REASON_RENAME_OLD_NAME |
                             Win32USN.USNReason.USN_REASON_REPARSE_POINT_CHANGE |
                             Win32USN.USNReason.USN_REASON_SECURITY_CHANGE |
                             Win32USN.USNReason.USN_REASON_STREAM_CHANGE,
                ReturnOnlyOnClose = 0,
                Timeout           = 0,
                BytesToWaitFor    = 0,
                UsnJournalID      = m_journal.UsnJournalID
            };

            var bufferSize = 4096; // larger buffer returns more record, but pervents user from cancelling operation for a longer time

            while (readData.StartUsn < m_journal.NextUsn)
            {
                if (!Win32USN.ControlWithInput(m_volumeHandle, Win32USN.FsCtl.ReadUSNJournal,
                                               ref readData, bufferSize, out var entryData))
                {
                    var e = Marshal.GetLastWin32Error();
                    if (e == Win32USN.ERROR_HANDLE_EOF || e == Win32USN.ERROR_SUCCESS)
                    {
                        break;
                    }

                    if (e == Win32USN.ERROR_INSUFFICIENT_BUFFER)
                    {
                        bufferSize = bufferSize * 2;
                        continue;
                    }

                    if (e == Win32USN.ERROR_JOURNAL_ENTRY_DELETED)
                    {
                        throw new UsnJournalSoftFailureException(Strings.USNHelper.JournalEntriesDeleted, new Win32Exception(e));
                    }

                    throw new Win32Exception(e);
                }

                records.AddRange(EnumerateRecords(entryData).TakeWhile(rec => rec.UsnRecord.Usn >= startUsn && rec.UsnRecord.Usn < m_journal.NextUsn));
                readData.StartUsn = Marshal.ReadInt64(entryData, 0);
            }

            return(records);
        }