/// <summary> /// Gets the next incremental/diff entry to backup. /// </summary> /// <returns> /// and IFSEntry /// </returns> /// <param name='path'> /// Path. /// </param> /// <param name='snapshottedPath'> /// Snapshotted path. /// </param> public IEnumerable <IFSEntry> GetNextEntry(BasePath path, string snapshottedPath) { //To efficiently reuse existing code, we instanciate // a "phantom" BackupRootDriveHandler an use its "GetFull()" FS items provider as an items source to check // for modifications. // Each entry already existing in the ref backup index is marked as 'found' (Tuple.Item2 = true) // to allow to detect not found items (deleted) after collecting curent FS items. phantomBrd = new BackupRootDriveHandler(rootDrive, this.taskId, 0, 0, 0, BackupLevel.Full, 0, 0, 0); phantomBrd.SetCurrentPath(path); phantomBrd.SubCompletionEvent += new BackupRootDriveHandler.SubCompletionHandler(BubbleUpSubCompletion); foreach (IFSEntry entry in phantomBrd.GetFull(true)) { // New File (inode/id > last refMaxId known inode/id) or doesn't exist in ref backup if (entry.ID > refMaxId || !refEntries.ContainsKey(entry.ID)) //todo : also check CreationTime? { Console.WriteLine("Incremental GetFilesToBackup() added NEW entry : " + entry.SnapFullPath); entry.ChangeStatus = DataLayoutInfos.New; yield return(entry); continue; } else if (entry.LastModifiedTime > refEntries[entry.ID].Item1) { entry.ChangeStatus = DataLayoutInfos.HasChanges; refEntries[entry.ID].Item2 = true; yield return(entry); continue; } else if (entry.LastMetadataModifiedTime > refBackupTimeStart) { entry.ChangeStatus = DataLayoutInfos.MetadaOnly; refEntries[entry.ID].Item2 = true; yield return(entry); continue; } else { refEntries[entry.ID].Item2 = true; } } // // end foreach phantomBrdh // last step : returd deleted entries foreach (var item in refEntries) { if (!item.Value.Item2) { IFSEntry deleted = prov.GetEmptyItem(); deleted.ID = item.Key; deleted.ChangeStatus = DataLayoutInfos.Deleted; yield return(deleted); } } refEntries = null; yield break; }
/// <summary> /// Gets the next incremental/diff entry to backup. To efficiently reuse existing code, we instanciate /// a "phantom" BackupRootDriveHandler an use its "GetFull()" FS items provider as an items source to check /// for modifications. /// </summary> /// <returns> /// and IFSEntry /// </returns> /// <param name='path'> /// Path. /// </param> /// <param name='snapshottedPath'> /// Snapshotted path. /// </param> public IEnumerable <IFSEntry> GetNextEntry(BasePath path, string snapshottedPath) { idsToWatch = new List <long>(); fsProv = FSEnumeratorProvider.GetFSEnumeratorProvider().GetFSEnumerator(snapshottedPath); phantomBrd = new BackupRootDriveHandler(rootDrive, this.taskId, 0, 0, 0, BackupLevel.Full, 0, 0, 0); phantomBrd.SetCurrentPath(path); phantomBrd.SubCompletionEvent += new BackupRootDriveHandler.SubCompletionHandler(BubbleUpSubCompletion); bool moveRefEnumerator = true; IFSEntry refEntry = null; IFSEntry realRefEntry = null; foreach (IFSEntry entry in phantomBrd.GetFull(true)) { Console.WriteLine("PhantomBrd current entry : " + entry.Name); // New File (inode/id > last refMaxId known inode/id) if (entry.ID > refMaxId /*|| entry.CreateTime > refBackupTimeStart*/) { Console.WriteLine("Incremental GetFilesToBackup() added NEW entry : " + entry.SnapFullPath); yield return(entry); continue; } //try{ //TOREMOVE if (moveRefEnumerator || refEntry == null) { if (idxEnumerator.MoveNext()) // try to position to the same entry, previously backuped /*if(refEntry != null && refEntry.ID == idxEnumerator.Current.ID){ * while(idxEnumerator.Current.ID == refEntry.ID) // in case we meet BigFiles (multiple same id entries), loop. * if(!idxEnumerator.MoveNext()) * break; * }*/ //else { refEntry = idxEnumerator.Current; Console.WriteLine("Ref entry : " + refEntry.Name); } else { Console.WriteLine("CANNOT MoveNext() -- last entry is " + refEntry.Name); } //firstMove = false; } else { moveRefEnumerator = true; Console.WriteLine("moveRef = false"); } /*} * catch(Exception e){ // TOREMOVE * Console.WriteLine ("Incremental GetFilesToBackup() ERROR : "+e.ToString()); * }*/ //Console.WriteLine("Incremental GetFilesToBackup() ------- Refentry id="+refEntry.ID+", name="+refEntry.Name+", curid="+entry.ID+", curname="+entry.Name); //Console.WriteLine ("RefEntry: "+refEntry.ToString()+", curEntry: "+entry.ToString()); //if(refEntry.ID == entry.ID) // Console.WriteLine("Incremental GetFilesToBackup() entry "+entry.SnapFullPath+" __MATCHED__ in ref backup"); if (refEntry.ID != entry.ID) { Console.WriteLine("Incremental cur entry " + entry.ToString() + " DOESN'T_MATCH ref = " + refEntry.ToString()); long refItemPos = 0; // new file potentially reusing an inode/ID number < ref max ID if (entry.CreateTime > refBackupTimeStart || entry.LastModifiedTime > refBackupTimeStart) { moveRefEnumerator = false; yield return(entry); continue; } //other cases : File RenamedOrMovedItem RenamedOrMovedItem deleted realRefEntry = refIndex.SearchItem(entry, rootDrive.SystemDrive.MountPoint, out refItemPos); if (realRefEntry == null) { moveRefEnumerator = false; yield return(entry); continue; } // check if entry has been moved from outside to the current dir else { refEntry = realRefEntry; if (refEntry.ParentID != entry.ParentID) // moved { moveRefEnumerator = false; //entry.ChangeStatus = //continue; } } } //refEntry = srch; // Entry already existed under the same id. if it also has the same name, continue checks. // else, it may have been (1)renamed, or (2)deleted + inode reused for new file. // (1) it it safe to assume it as only renamed if lastmetadata has changed but data hasn't. // (2) if oldname!=newname, and lastwritetime has change, we cannot decide what happened. Consider // it as a new entry, for safety. //Console.WriteLine("\t Search found matching ID, ref name="+srch.Name); /*if(srch.Name != entry.Name){ * Console.WriteLine("Incremental GetFilesToBackup() added RENAMED entry : "+entry.SnapFullPath); * entry.BlockMetadata.BlockMetadata.Add(new RenamedOrMovedItem()); * yield return entry; * continue; * }*/ // Existing entry with modified data since ref backup // lastmod < ref lastmod : rename(?). lastmod < ref lastmod : entry data modified if (entry.LastModifiedTime != /*refBackupTimeStart*/ refEntry.LastModifiedTime || entry.FileSize != refEntry.FileSize) { Console.WriteLine("Incremental GetFilesToBackup() added MODIFIED (LastModifiedTime) entry : " + entry.SnapFullPath + ", entry.LastModifiedTime=" + entry.LastModifiedTime + ",refEntry.LastModifiedTime=" + refEntry.LastModifiedTime); entry.ChangeStatus = DataLayoutInfos.HasChanges; yield return(entry); continue; } else if (entry.LastMetadataModifiedTime > refEntry.LastMetadataModifiedTime) { entry.ChangeStatus = DataLayoutInfos.MetadaOnly; // moved entry //if(srch.ParentID != entry.ParentID || srch.Name != entry.Name){ //entry.BlockMetadata.BlockMetadata.Add(new RenamedOrMovedItem()); //entry.ChangeStatus = DataLayoutInfos.MetadaOnly; // .RenameOnly; //} //else // entry.BlockMetadata.BlockMetadata.Add(new UnchangedDataItem()); Console.WriteLine("Incremental GetFilesToBackup() added METADATACHANGE (LastMetadataModifiedTime) entry : " + entry.SnapFullPath); yield return(entry); continue; } // if we get there, entry hasn't changed. //Console.WriteLine("Incremental GetFilesToBackup UNCHANGED "+entry.Name); // } // If we get there, FS entry didn't change since last backup. // But if we are asked to perform a full refresh, return it anyway, with appropriate ChangeFlag entry.ChangeStatus = DataLayoutInfos.NoChange; yield return(entry); } // // end foreach phantomBrdh Console.WriteLine("FileCompareProvider GetNextEntry : end foreach"); // Fs has been enumerated. Now check if some ref index IDs remain unfound (== present inside idsToWatch) // If so, either they have been deleted, or they have been moved out of scope (out of FS backup root directory) // Anyways, we tag them as 'deleted', as, from the backup's root folder point of view, they are not part of // the backup anymore. if (idsToWatch.Count > 0) { Logger.Append(Severity.DEBUG, idsToWatch.Count + " entries seem to have been deleted."); } foreach (long id in idsToWatch) { long useless = 0; IFSEntry deleted = refIndex.SearchItem(id, rootDrive.SystemDrive.MountPoint, out useless); if (deleted != null) { Console.WriteLine("\t entry " + id + " has been deleted, was " + deleted.Name); deleted.ChangeStatus = DataLayoutInfos.Deleted; yield return(deleted); } else { Console.WriteLine("\t entry " + id + " has NOT BEEN DELETED - ERROR!!!"); } } yield break; }
/// <summary> /// Gets the next incremental/diff entry to backup. To efficiently reuse existing code, we instanciate /// a "phantom" BackupRootDriveHandler an use its "GetFull()" FS items provider as an items source to check /// for modifications. /// </summary> /// <returns> /// and IFSEntry /// </returns> /// <param name='path'> /// Path. /// </param> /// <param name='snapshottedPath'> /// Snapshotted path. /// </param> public IEnumerable <IFSEntry> GetNextEntry(BasePath path, string snapshottedPath) { idsToWatch = new List <long>(); fsProv = FSEnumeratorProvider.GetFSEnumeratorProvider().GetFSEnumerator(snapshottedPath); phantomBrd = new BackupRootDriveHandler(rootDrive, this.taskId, 0, 0, 0, BackupLevel.Full, 0, 0, 0); phantomBrd.SetCurrentPath(path); phantomBrd.SubCompletionEvent += new BackupRootDriveHandler.SubCompletionHandler(BubbleUpSubCompletion); bool moveRefEnumerator = true; IFSEntry refEntry = null; IFSEntry realRefEntry = null; foreach (IFSEntry entry in phantomBrd.GetFull(true)) { Console.WriteLine("PhantomBrd current entry : " + entry.Name); // New File (inode/id > last refMaxId known inode/id) if (entry.ID > refMaxId /*|| entry.CreateTime > refBackupTimeStart*/) { Console.WriteLine("Incremental GetFilesToBackup() added NEW entry : " + entry.SnapFullPath); yield return(entry); continue; } try{ //TOREMOVE if (moveRefEnumerator || refEntry == null) { if (idxEnumerator.MoveNext()) // try to position to the same entry, previously backuped /*if(refEntry != null && refEntry.ID == idxEnumerator.Current.ID){ * while(idxEnumerator.Current.ID == refEntry.ID) // in case we meet BigFiles (multiple same id entries), loop. * if(!idxEnumerator.MoveNext()) * break; * }*/ //else { refEntry = idxEnumerator.Current; Console.WriteLine("Ref entry : " + refEntry.Name); } else { Console.WriteLine("CANNOT MoveNext() -- last entry is " + refEntry.Name); } //firstMove = false; } else { moveRefEnumerator = true; Console.WriteLine("moveRef = false"); } } catch (Exception e) { // TOREMOVE Console.WriteLine("Incremental GetFilesToBackup() ERROR : " + e.ToString()); } //Console.WriteLine("Incremental GetFilesToBackup() ------- Refentry id="+refEntry.ID+", name="+refEntry.Name+", curid="+entry.ID+", curname="+entry.Name); //Console.WriteLine ("RefEntry: "+refEntry.ToString()+", curEntry: "+entry.ToString()); //if(refEntry.ID == entry.ID) // Console.WriteLine("Incremental GetFilesToBackup() entry "+entry.SnapFullPath+" __MATCHED__ in ref backup"); if (refEntry.ID != entry.ID) { Console.WriteLine("Incremental cur entry " + entry.ToString() + " DOESN'T_MATCH ref = " + refEntry.ToString()); long refItemPos = 0; // new file potentially reusing an inode/ID number < ref max ID if (entry.CreateTime > refBackupTimeStart || entry.LastModifiedTime > refBackupTimeStart) { moveRefEnumerator = false; yield return(entry); continue; } //other cases : File RenamedOrMovedItem RenamedOrMovedItem deleted realRefEntry = refIndex.SearchItem(entry, rootDrive.SystemDrive.MountPoint, out refItemPos); if (realRefEntry == null) { moveRefEnumerator = false; yield return(entry); continue; } // check if entry has been moved from outside to the current dir else { refEntry = realRefEntry; if (refEntry.ParentID != entry.ParentID) // moved { moveRefEnumerator = false; //entry.ChangeStatus = //continue; } } /*// Check if entry is a newly created file (ctime > last backup start time) but with an ''old'' mtime * if(refIndex.SearchItem(entry, rootDrive.SystemDrive.MountPoint, out searchRowid) == null * && entry.LastMetadataModifiedTime > refBackupTimeStart){ * Console.WriteLine("Found (new?) entry with reused ID : "+entry.ToString()); * * }*/ // else check if refEntry has been deleted /*else{ * Console.WriteLine("Incremental GetFilesToBackup() entry "+refEntry.ToString()+" __DELETED__"); * refEntry.ChangeStatus = DataLayoutInfos.Deleted; * yield return refEntry; * continue; * * }*/ /*if(idsToWatch.Contains(entry.ID)){ // we found it! is was simply moved * Logger.Append (Severity.DEBUG2, "Found wanted entry "+entry.ID+", "+entry.SnapFullPath); * for(int i=idsToWatch.Count-1; i==0; i--) * if(idsToWatch[i] == entry.ID) * idsToWatch.RemoveAt(i); * }*/ //Console.WriteLine("Incremental GetFilesToBackup() entry "+entry.SnapFullPath+"("+entry.ID+") DOES NOT MATCH, got "+refEntry.Name+" ("+refEntry.ID+")"); /*long searchRowid = 0; * if((refEntry = refIndex.SearchItem(entry, rootDrive.SystemDrive.MountPoint, out searchRowid)) != null){ * long fsToIndexOffset = searchRowid - refIndex.RowId; * Console.WriteLine("Incremental GetFilesToBackup() Found ref entry "+entry.SnapFullPath+" at __OFFSET__="+(searchRowid - refIndex.RowId)); * if( fsToIndexOffset < 100){ * * for (int j = (int)fsToIndexOffset; j >0; j--){ * if(idxEnumerator.MoveNext()) * idsToWatch.Add(idxEnumerator.Current.ID); * } * } * //if current FS entry and ref entry mismatch, but current FS entry exists in ref index , * // we put this ref entry on the 'to watch' list : * // It may indicate that ref has been deleted, or, if we meet this 'watched' id during backup, that * // the item has been moved * else{ * //idsToWatch.Add(refEntry.ID); * idxEnumerator.Dispose(); * idxEnumerator = refIndex.GetBaseItemsEnumerator(rootDrive.SystemDrive.OriginalMountPoint, refIndex.RowId).GetEnumerator(); * idxEnumerator.MoveNext(); * Console.WriteLine ("moved ref index enumerator to new root "+idxEnumerator.Current.ID+", name="+idxEnumerator.Current.Name); * } * }*/ /*else{ // new entry reusing "old" inode number/ID * Console.WriteLine("\t NOT found matching ID for name="+entry.Name); * yield return entry; * // this new entry could explain the offset we get between current fs and ref index * moveRefEnumerator = false; * continue; * }*/ } //refEntry = srch; // Entry already existed under the same id. if it also has the same name, continue checks. // else, it may have been (1)renamed, or (2)deleted + inode reused for new file. // (1) it it safe to assume it as only renamed if lastmetadata has changed but data hasn't. // (2) if oldname!=newname, and lastwritetime has change, we cannot decide what happened. Consider // it as a new entry, for safety. //Console.WriteLine("\t Search found matching ID, ref name="+srch.Name); /*if(srch.Name != entry.Name){ * Console.WriteLine("Incremental GetFilesToBackup() added RENAMED entry : "+entry.SnapFullPath); * entry.BlockMetadata.BlockMetadata.Add(new RenamedOrMovedItem()); * yield return entry; * continue; * }*/ // Existing entry with modified data since ref backup // lastmod < ref lastmod : rename(?). lastmod < ref lastmod : entry data modified if (entry.LastModifiedTime != /*refBackupTimeStart*/ refEntry.LastModifiedTime || entry.LastMetadataModifiedTime != refEntry.LastMetadataModifiedTime) { Console.WriteLine("Incremental GetFilesToBackup() added MODIFIED (LastModifiedTime) entry : " + entry.SnapFullPath + ", entry.LastModifiedTime=" + entry.LastModifiedTime + ",refEntry.LastModifiedTime=" + refEntry.LastModifiedTime); entry.ChangeStatus = DataLayoutInfos.HasChanges; yield return(entry); continue; } /*else if(entry.LastMetadataModifiedTime > refEntry.LastMetadataModifiedTime){ * entry.ChangeStatus = DataLayoutInfos.MetadaOnly; * // moved entry * //if(srch.ParentID != entry.ParentID || srch.Name != entry.Name){ * //entry.BlockMetadata.BlockMetadata.Add(new RenamedOrMovedItem()); * entry.ChangeStatus = DataLayoutInfos.MetadaOnly; // .RenameOnly; * //} * //else * // entry.BlockMetadata.BlockMetadata.Add(new UnchangedDataItem()); * Console.WriteLine("Incremental GetFilesToBackup() added METADATACHANGE (LastMetadataModifiedTime) entry : "+entry.SnapFullPath); * yield return entry; * continue; * }*/ // if we get there, entry hasn't changed. //Console.WriteLine("Incremental GetFilesToBackup UNCHANGED "+entry.Name); // } // If we get there, FS entry didn't change since last backup. // But if we are asked to perform a full refresh, return it anyway, with appropriate ChangeFlag if (this.isFullRefreshBackup) { entry.ChangeStatus = DataLayoutInfos.NoChange; yield return(entry); } } // // end foreach phantomBrdh Console.WriteLine("FileCompareProvider GetNextEntry : end foreach"); // Fs has been enumerated. Now check if some ref index IDs remain unfound (== present inside idsToWatch) // If so, either they have been deleted, or they have been moved out of scope (out of FS backup root directory) // Anyways, we tag them as 'deleted', as, from the backup's root folder point of view, they are not part of // the backup anymore. if (idsToWatch.Count > 0) { Logger.Append(Severity.DEBUG, idsToWatch.Count + " entries seem to have been deleted."); } foreach (long id in idsToWatch) { long useless = 0; IFSEntry deleted = refIndex.SearchItem(id, rootDrive.SystemDrive.MountPoint, out useless); if (deleted != null) { Console.WriteLine("\t entry " + id + " has been deleted, was " + deleted.Name); deleted.ChangeStatus = DataLayoutInfos.Deleted; yield return(deleted); } else { Console.WriteLine("\t entry " + id + " has NOT BEEN DELETED - ERROR!!!"); } } yield break; }