Ejemplo n.º 1
0
        public PackageHashes(ClientVersion version, IEnumerable <Hash> segmentHashes, CryptoProvider cryptoProvider, PackageSequenceInfo packageSequence)
        {
            if (cryptoProvider == null)
            {
                throw new ArgumentNullException(nameof(cryptoProvider));
            }

            if (packageSequence == null)
            {
                throw new ArgumentNullException(nameof(packageSequence));
            }

            Version = version;
            PackageSegmentsHashes = segmentHashes.ToArray();
            PackageId             = cryptoProvider.HashFromHashes(PackageSegmentsHashes);
            PackageSize           = packageSequence.PackageSize;
            SegmentLength         = packageSequence.SegmentLength;
            DataFileLength        = packageSequence.DataFileLength;
        }
Ejemplo n.º 2
0
        private async Task <PackageDataValidatorResult> ValidatePackageAsyncInternal(LocalPackageInfo packageInfo)
        {
            if (packageInfo == null)
            {
                throw new ArgumentNullException(nameof(packageInfo));
            }

            if (!packageInfo.DownloadStatus.IsDownloaded)
            {
                // remark: this can be implemented but don't need it now
                throw new InvalidOperationException("Can't validate integrity of not fully downloaded package.");
            }

            // basic input data integrity validations
            if (packageInfo.Hashes.PackageSize != packageInfo.Sequence.PackageSize)
            {
                return(PackageDataValidatorResult.WithError("Hashes file provided invalid package size that does not match with sequence."));
            }

            if (packageInfo.Metadata.PackageSize != packageInfo.Sequence.PackageSize)
            {
                return(PackageDataValidatorResult.WithError("Metadata file provided invalid package size that does not match with sequence."));
            }

            if (packageInfo.Sequence.SegmentsCount != packageInfo.Hashes.PackageSegmentsHashes.Length)
            {
                return(PackageDataValidatorResult.WithError("Hashes file provided invalid count of segments that does not match with sequence."));
            }

            // validate package hash calculated from segment hashes
            var calculatedPackageHash = cryptoProvider.HashFromHashes(packageInfo.Hashes.PackageSegmentsHashes);

            if (!calculatedPackageHash.Equals(packageInfo.Id))
            {
                return(PackageDataValidatorResult.WithError($"Hash mismatch. Calculated package hash is {calculatedPackageHash:s} but expected is {packageInfo.Id:s}."));
            }

            // before working with files - obtain lock to make sure package is not deleted on check
            if (!packageInfo.LockProvider.TryLock(out object lockToken))
            {
                throw new InvalidOperationException("Can't obtain lock for this package. It is marked for deletion.");
            }
            try
            {
                // start checking files
                var errors    = new List <string>();
                var sequencer = new PackagePartsSequencer();

                // check if data files exists and if correct size
                foreach (var dataFile in sequencer.GetDataFilesForPackage(packageInfo.Reference.FolderPath, packageInfo.Sequence))
                {
                    try
                    {
                        var fileInfo = new FileInfo(dataFile.Path);
                        if (!fileInfo.Exists)
                        {
                            errors.Add($"Expected data file not found. File: {dataFile.Path}");
                            continue;
                        }

                        if (fileInfo.Length != dataFile.DataFileLength)
                        {
                            errors.Add($"Invalid length of data file. Expected is {dataFile.DataFileLength}b but actual is {fileInfo.Length}b. File: {dataFile.Path}");
                            continue;
                        }
                    }
                    catch (Exception e)
                    {
                        errors.Add($"Can't validate file \"{ dataFile.Path }\". Reason: {e.Message}");
                    }
                }

                // don't continue if files are not OK
                if (errors.Any())
                {
                    return(PackageDataValidatorResult.WithErrors(errors));
                }

                // do file hashes check
                IEnumerable <PackageDataStreamPart> allParts = sequencer.GetPartsForPackage(packageInfo.Reference.FolderPath, packageInfo.Sequence);
                try
                {
                    using (var readPackageController = new ReadPackageDataStreamController(loggerFactory, packageInfo.Reference, packageInfo.Sequence, allParts))
                        using (var readPackageStream = new PackageDataStream(loggerFactory, readPackageController))
                            using (var validatePackageController = new ValidatePackageDataStreamController(loggerFactory, cryptoProvider, packageInfo.Sequence, packageInfo.Hashes, allParts, nestedStream: null))
                                using (var validatePackageStream = new PackageDataStream(loggerFactory, validatePackageController))
                                {
                                    await readPackageStream.CopyToAsync(validatePackageStream);
                                }
                }
                catch (HashMismatchException e)
                {
                    errors.Add($"Data file segment hash mismatch: {e.Message}");
                }
                catch (Exception e)
                {
                    errors.Add($"Can't process data files to validation. {e.ToString()}");
                }

                // get result
                if (errors.Any())
                {
                    return(PackageDataValidatorResult.WithErrors(errors));
                }
                return(PackageDataValidatorResult.Valid);
            }
            finally
            {
                packageInfo.LockProvider.Unlock(lockToken);
            }
        }