/// <summary> /// </summary> /// <param name="File"></param> /// <param name="Offset"></param> /// <param name="Size"></param> /// <returns></returns> public bool GetBlockInfo(int Index, ref BuildManifestBlockInfo Info) { LazyCacheBlockInfo(); if (Index < 0 || Index >= BlockCount) { throw new ArgumentOutOfRangeException("Index", "Block index out of range."); } Info = BlockInfo[Index]; return(true); }
/// <summary> /// </summary> /// <param name="Index"></param> /// <returns></returns> public bool GetBlockData(int Index, string RootPath, AsyncIOQueue IOQueue, byte[] Data, out long DataLength) { LazyCacheBlockInfo(); DataLength = 0; if (Index < 0 || Index >= BlockCount) { throw new ArgumentOutOfRangeException("Index", "Block index out of range."); return(false); } BuildManifestBlockInfo Info = BlockInfo[Index]; Debug.Assert(Data.Length >= Info.TotalSize); DataLength = Info.TotalSize; ManualResetEvent CompleteEvent = new ManualResetEvent(false); int QueuedReads = Info.SubBlocks.Length; bool Success = true; foreach (BuildManifestSubBlockInfo SubBlock in Info.SubBlocks) { string PathName = Path.Combine(RootPath, SubBlock.File.Path); IOQueue.Read( PathName, SubBlock.FileOffset, SubBlock.FileSize, Data, SubBlock.OffsetInBlock, bSuccess => { int Result = Interlocked.Decrement(ref QueuedReads); if (Result == 0) { CompleteEvent.Set(); } if (!bSuccess) { Success = false; Logger.Log(LogLevel.Error, LogCategory.Manifest, "Failed to read data for block (offset={0} size={1}) from file {2}", SubBlock.FileOffset, SubBlock.FileSize, SubBlock.File); } } ); } CompleteEvent.WaitOne(); return(Success); }
/// <summary> /// </summary> public List <int> Validate(string RootPath, RateTracker Tracker, AsyncIOQueue IOQueue, BuildManfiestValidateProgressCallbackHandler Callback = null) { LazyCacheBlockInfo(); List <int> FailedBlocks = new List <int>(); try { LockBlockInfo(); int TaskCount = Environment.ProcessorCount; Task[] FileTasks = new Task[TaskCount]; int BlockCounter = 0; long BytesValidated = 0; bool Aborted = false; // Check the size of each file. for (int i = 0; i < Files.Count; i++) { BuildManifestFileInfo FileInfo = Files[i]; string FilePath = Path.Combine(RootPath, FileInfo.Path); string DirPath = Path.GetDirectoryName(FilePath); if (!Directory.Exists(DirPath)) { Directory.CreateDirectory(DirPath); Logger.Log(LogLevel.Warning, LogCategory.Manifest, "Expected directory {0} does not exist, creating.", DirPath); } FileInfo Info = new FileInfo(FilePath); if (!Info.Exists || Info.Length != FileInfo.Size) { using (FileStream Stream = new FileStream(FilePath, FileMode.Create, FileAccess.ReadWrite, FileShare.Read)) { Logger.Log(LogLevel.Warning, LogCategory.Manifest, "File {0} is not of expected length {1} (is {2}) settting length.", FilePath, FileInfo.Size, Info.Length); Stream.SetLength(FileInfo.Size); } } } // Check each individual block of data for validity. for (int i = 0; i < TaskCount; i++) { FileTasks[i] = Task.Run( () => { byte[] Buffer = new byte[BlockSize]; while (!Aborted) { int BlockIndex = Interlocked.Increment(ref BlockCounter) - 1; if (BlockIndex >= BlockCount) { break; } BuildManifestBlockInfo BInfo = BlockInfo[BlockIndex]; long BufferLength = 0; bool Success = GetBlockData(BlockIndex, RootPath, IOQueue, Buffer, out BufferLength); uint Checksum = 0; if (Version >= 2) { Checksum = Crc32Fast.Compute(Buffer, 0, (int)BufferLength); } else { Checksum = Crc32Slow.Compute(Buffer, (int)BufferLength); } if (!Success || BlockChecksums[BlockIndex] != Checksum) { Logger.Log(LogLevel.Warning, LogCategory.Manifest, "Block {0} failed checksum, block contains following sub-blocks:", BlockIndex); for (int SubBlock = 0; SubBlock < BInfo.SubBlocks.Length; SubBlock++) { BuildManifestSubBlockInfo SubBInfo = BInfo.SubBlocks[SubBlock]; Logger.Log(LogLevel.Warning, LogCategory.Manifest, "\tfile={0} offset={1} size={2}", SubBInfo.File.Path, SubBInfo.FileOffset, SubBInfo.FileSize); } lock (FailedBlocks) { FailedBlocks.Add(BlockIndex); } } Interlocked.Add(ref BytesValidated, BInfo.TotalSize); if (Callback != null) { if (!Callback.Invoke(BytesValidated, TotalSize, Guid, BlockIndex)) { Aborted = true; } } } } ); } Task.WaitAll(FileTasks); } finally { UnlockBlockInfo(); } return(FailedBlocks); }