private Dictionary <int, Win32Api.UsnEntry> GetUsnRecordsDictionary() { PrivilegesManager pm = new PrivilegesManager(); pm.Grant(); Dictionary <int, Win32Api.UsnEntry> uEntries = new Dictionary <int, Win32Api.UsnEntry>(); using (usnJ = new NtfsUsnJournal(/*brd.SystemDrive.MountPoint*/ brd /*.Snapshot.MountPoint*/)){ Logger.Append(Severity.DEBUG, "Reading USN journal " + journalId + " for '" + brd.SystemDrive.MountPoint + "' from seq " + prevTransactionId + " to seq " + transactionId + " (changed entries from " + Utilities.Utils.GetLocalDateTimeFromUnixTime(refTimeStamp).ToString() + " to " + Utilities.Utils.GetLocalDateTimeFromUnixTime(brd.Snapshot.TimeStamp).ToLocalTime().ToString() + ")"); Win32Api.USN_JOURNAL_DATA stateJd = new Win32Api.USN_JOURNAL_DATA(); stateJd.UsnJournalID = journalId; stateJd.NextUsn = prevTransactionId; Win32Api.USN_JOURNAL_DATA newState = new Win32Api.USN_JOURNAL_DATA(); // unused, as we maintain our own state List <Win32Api.UsnEntry> changedUsnEntries = new List <Win32Api.UsnEntry>(); usnJ.GetUsnJournalState(ref newState); NtfsUsnJournal.UsnJournalReturnCode retCode = usnJ.GetUsnJournalEntries(stateJd, refTimeStamp, 0xFFFFFFFF, out changedUsnEntries, out newState); if (retCode != NtfsUsnJournal.UsnJournalReturnCode.USN_JOURNAL_SUCCESS) { throw new Exception(retCode.ToString()); } int entryId = 0; foreach (Win32Api.UsnEntry ue in changedUsnEntries) { if (ue != null && ue.Reason > 0) { entryId = (int)(ue.FileReferenceNumber); //if(ue.Name.StartsWith("grut")) //Console.WriteLine ("|--------| USN seq="+ue.USN+", item "+entryId+" ("+ue.Name+") "+((NtfsUsnJournal.UsnReasonCode)ue.Reason).ToString()); if (!uEntries.ContainsKey(entryId)) { uEntries[entryId] = ue; } else // cumulate reason flags // ignore created+deleted (temporary or short-lived (between 2 backups) items { if ( ((Win32Api.UsnReasonCode)ue.Reason).HasFlag(Win32Api.UsnReasonCode.USN_REASON_FILE_DELETE) && ((Win32Api.UsnReasonCode)uEntries[entryId].Reason).HasFlag(Win32Api.UsnReasonCode.USN_REASON_FILE_CREATE) ) { Console.WriteLine("*** item " + ue.Name + " CREATED+DELETED"); continue; } // file ID reused (file delete + new create) : totally replace previous entry else if ( ((Win32Api.UsnReasonCode)ue.Reason).HasFlag(Win32Api.UsnReasonCode.USN_REASON_FILE_CREATE) && ((Win32Api.UsnReasonCode)uEntries[entryId].Reason).HasFlag(Win32Api.UsnReasonCode.USN_REASON_FILE_DELETE) ) { uEntries[entryId] = ue; } // cumulate flags else if (!((Win32Api.UsnReasonCode)uEntries[entryId].Reason).HasFlag(((Win32Api.UsnReasonCode)ue.Reason))) { Win32Api.UsnReasonCode newReason = ((Win32Api.UsnReasonCode)uEntries[entryId].Reason) | ((Win32Api.UsnReasonCode)ue.Reason); uEntries[entryId] = ue; uEntries[entryId].Reason = (uint)newReason; } // only keep the last rename operation /*if(((NtfsUsnJournal.UsnReasonCode)ue.Reason).HasFlag(NtfsUsnJournal.UsnReasonCode.USN_REASON_RENAME_NEW_NAME) ){ * Console.WriteLine ("*** item "+ue.Name+" RENAMED (reasons="+((NtfsUsnJournal.UsnReasonCode)ue.Reason).ToString()); * NtfsUsnJournal.UsnReasonCode newReason = ((NtfsUsnJournal.UsnReasonCode)entries[entryId].Reason) ; * if(!((NtfsUsnJournal.UsnReasonCode)entries[entryId].Reason).HasFlag(NtfsUsnJournal.UsnReasonCode.USN_REASON_RENAME_NEW_NAME) ) * newReason |= NtfsUsnJournal.UsnReasonCode.USN_REASON_RENAME_NEW_NAME; * entries[entryId] = ue; * entries[entryId].Reason = (uint)newReason; * }*/ } } } Logger.Append(Severity.TRIVIA, "Done reading USN journal " + journalId + " for '" + brd.SystemDrive.MountPoint); } //end using return(uEntries); }
public IEnumerable <IFSEntry> GetNextEntry(BasePath path, string snapshottedPath) { IFileProvider itemProv = ItemProvider.GetProvider(); try{ if (entries == null) { entries = GetUsnRecordsDictionary(); } Logger.Append(Severity.TRIVIA, "Got " + entries.Count + " (total for whole drive) changed entries since reference backup"); } catch (Exception e) { Logger.Append(Severity.ERROR, "Cannot query drive USN journal : " + e.Message); throw(e); } // now that we got a list with only the last usn cumulated change, analyze it foreach (KeyValuePair <int, Win32Api.UsnEntry> entry in entries) { string ePath = ""; Console.WriteLine(" USN entry: " + entry.ToString()); usnJ.GetPathFromFileReference(entry.Value.FileReferenceNumber /*(ulong)entry.Key*/, out ePath); ePath = brd.Snapshot.MountPoint /*Path*/.TrimEnd('\\') + ePath; //if(ePath.ToLower().Contains("copy") ||ePath.ToLower().Contains("docs") ) Console.WriteLine(" ///// fullpath=" + ePath); IFSEntry changedItem; /*if(((NtfsUsnJournal.UsnReasonCode)entry.Value.Reason).HasFlag(NtfsUsnJournal.UsnReasonCode.USN_REASON_FILE_CREATE)) * Console.WriteLine ("*** item "+ePath+" CREATED"); * else */ if ((((Win32Api.UsnReasonCode)entry.Value.Reason).HasFlag(Win32Api.UsnReasonCode.USN_REASON_FILE_DELETE))) { /*if( (((NtfsUsnJournal.UsnReasonCode)entry.Value.Reason).HasFlag ( NtfsUsnJournal.UsnReasonCode.USN_REASON_FILE_CREATE )) ){ * Console.WriteLine ("*** item "+entry.Value.Name+" CREATED+DELETED"); * // TODO : handle deleted + created items (ID reused), if NTFS does so * continue; * }*/ Console.WriteLine("***DELETEF### item " + entry.Value.Name + " DELETED"); changedItem = new NTBackupFile(); changedItem.ID = entry.Key; changedItem.Name = entry.Value.Name; //changedItem.BlockMetadata.BlockMetadata.Add(new DeletedItem()); changedItem.ChangeStatus = DataLayoutInfos.Deleted; yield return(changedItem); continue; } // item not corresponding to current wanted path. if (ePath.IndexOf(snapshottedPath) != 0) { continue; } try{ changedItem = itemProv.GetItemByPath(ePath); changedItem.ParentID = (int)entry.Value.ParentFileReferenceNumber; /*else/* if( (((NtfsUsnJournal.UsnReasonCode)entry.Value.Reason).HasFlag ( NtfsUsnJournal.UsnReasonCode.USN_REASON_DATA_EXTEND )) || (((NtfsUsnJournal.UsnReasonCode)entry.Value.Reason).HasFlag ( NtfsUsnJournal.UsnReasonCode.USN_REASON_DATA_OVERWRITE )) || (((NtfsUsnJournal.UsnReasonCode)entry.Value.Reason).HasFlag ( NtfsUsnJournal.UsnReasonCode.USN_REASON_DATA_TRUNCATION )) || (((NtfsUsnJournal.UsnReasonCode)entry.Value.Reason).HasFlag ( NtfsUsnJournal.UsnReasonCode.USN_REASON_ENCRYPTION_CHANGE )) || ) || { || Console.WriteLine ("*** item "+ePath+" DATA MODIFIED"); || changedItem.BlockMetadata.BlockMetadata.Add(new Mod()); || }*/ /*else */ if ((((Win32Api.UsnReasonCode)entry.Value.Reason).HasFlag(Win32Api.UsnReasonCode.USN_REASON_RENAME_NEW_NAME) || (((Win32Api.UsnReasonCode)entry.Value.Reason).HasFlag(Win32Api.UsnReasonCode.USN_REASON_RENAME_OLD_NAME)) )) { Console.WriteLine("***RENAMED### item " + ePath + " RENAMED "); //RenamedOrMovedItem rmi = new RenamedOrMovedItem(); //rmi.OldName = entry.Value.OldName; //changedItem.BlockMetadata.BlockMetadata.Add(rmi); // check if there is data changes , additionally to rename: Win32Api.UsnReasonCode newR = ((Win32Api.UsnReasonCode)entry.Value.Reason); newR &= ~Win32Api.UsnReasonCode.USN_REASON_RENAME_NEW_NAME; newR &= ~Win32Api.UsnReasonCode.USN_REASON_RENAME_OLD_NAME; newR &= ~Win32Api.UsnReasonCode.USN_REASON_CLOSE; if ((uint)newR == 0x00000000) { changedItem.ChangeStatus = DataLayoutInfos.RenameOnly; } //changedItem.BlockMetadata.BlockMetadata.Add(new UnchangedDataItem()); } /*else if( (((NtfsUsnJournal.UsnReasonCode)entry.Value.Reason).HasFlag ( NtfsUsnJournal.UsnReasonCode.U )) ) * Console.WriteLine ("*** item "+ePath+" MOVED");*/ } catch (Exception e) { //don't add item, but report it, put backup task in Warning state Logger.Append(Severity.ERROR, "Could not add item '" + ePath + "', TODO : REPORT REPORT!!. " + e.Message); continue; } //Console.WriteLine (" == usn entry path="+ePath+", reason="+((NtfsUsnJournal.UsnReasonCode)entry.Value.Reason).ToString()+" ("+entry.Value.Reason+"), ID="+ (int)(entry.Value.FileReferenceNumber)+", pid="+(int)entry.Value.ParentFileReferenceNumber); yield return(changedItem); } //} }