public void Allocate(string path, PackageSequenceInfo sequence, bool overwrite) { if (path == null) { throw new ArgumentNullException(nameof(path)); } if (sequence == null) { throw new ArgumentNullException(nameof(sequence)); } logger.LogInformation($"Allocating {SizeFormatter.ToString(sequence.PackageSize)} for package data in {path}"); Directory.CreateDirectory(path); // check disk space and throw error if not enough var driveInfo = new DriveInfo(Directory.GetDirectoryRoot(path)); long freeSpace = driveInfo.TotalFreeSpace; if (freeSpace < sequence.PackageSize) { throw new InvalidOperationException($"There is not enough disk space on drive {driveInfo.Name}. Free space is {SizeFormatter.ToString(freeSpace)} but required is {SizeFormatter.ToString(sequence.PackageSize)}."); } // prepare parts var sequencer = new PackagePartsSequencer(); var parts = sequencer.GetDataFilesForPackage(path, sequence).ToArray(); if (!overwrite) { // check if already exists foreach (var part in parts) { if (File.Exists(part.Path)) { throw new Exception($"File already exists: {part.Path}"); } } } // allocate foreach (var part in parts) { using (var fs = new FileStream(part.Path, overwrite ? FileMode.OpenOrCreate : FileMode.CreateNew, FileAccess.Write, FileShare.None)) { fs.SetLength(part.PartLength); } } logger.LogDebug("Allocation completed."); }
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); } }