<PackageReference Include="Relativity.Server.Transfer.SDK" Version="25000.0.6" />

FileShareTransferCommand

using Polly; using Relativity.Transfer.Enumeration.Native; using Relativity.Transfer.Exceptions; using Relativity.Transfer.Extensions; using Relativity.Transfer.FileShare.Issues; using Relativity.Transfer.FileShare.Resources; using System; using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Linq; using System.Threading; namespace Relativity.Transfer.FileShare { internal class FileShareTransferCommand : TransferPathCommandBase { private readonly FileShareClientConfiguration configuration; private bool? copyDirectories; private bool? copySubDirectories; private bool? overWriteFiles; private readonly FileshareTransferIssueBuilderFactory issueBuilderFactory; private bool CopyDirectories { get { if (!copyDirectories.HasValue) copyDirectories = configuration.CopyDirectories; return copyDirectories.Value; } } private bool CopySubDirectories { get { if (!copySubDirectories.HasValue) copySubDirectories = configuration.CopySubDirectories; return copySubDirectories.Value; } } private bool OverWriteFilesAtDestination { get { if (!overWriteFiles.HasValue) overWriteFiles = configuration.OverwriteFiles; return overWriteFiles.Value; } } public FileShareTransferCommand(ITransferLog log, ITransferRequest request, ITransferJobService jobService, IFileSystemService fileSystemService, FileShareClientConfiguration configuration) : base(log, request, jobService, fileSystemService, configuration) { if (log == null) throw new ArgumentNullException("log"); if (request == null) throw new ArgumentNullException("request"); if (configuration == null) throw new ArgumentNullException("configuration"); this.configuration = configuration; copySubDirectories = configuration.CopySubDirectories; overWriteFiles = configuration.OverwriteFiles; issueBuilderFactory = new FileshareTransferIssueBuilderFactory(this.configuration); } protected override void OnPreExecute(CancellationToken token) { if (!token.IsCancellationRequested) { if (string.IsNullOrWhiteSpace(configuration.FileShareUncPath)) throw new TransferException(FileShareStrings.WorkspaceFileShareEmptyExceptionMessage); IRetryStrategy retryStrategy = RetryStrategies.CreateFixedTimeStrategy(1); int retryCount = 0; RetrySyntax.WaitAndRetry(Policy.Handle<Exception>(), 3, retryStrategy.Calculation, (Action<Exception, TimeSpan, Context>)delegate(Exception exception, TimeSpan timespan, Context context) { base.Log.LogTransferWarning(exception, base.Request, "Retry - {Timespan} - File Share pre-execute failed.", timespan); base.Log.LogError(FileShareStrings.WorkspaceFileShareValidationMessage, configuration.FileShareUncPath, LogRedaction.OnPositions(default(int))); ITransferIssue issue = issueBuilderFactory.Create().CreateFromErrorMessage("The workspace UNC path validation check failed - check logs for more details.").WithAttributes(IssueAttributes.Warning) .WithMaxRetryAttempts(base.Configuration.MaxJobRetryAttempts) .WithRetryAttempt(retryCount) .Build(); RegisterIssue(issue); retryCount++; }).Execute((Action)delegate { if (base.FileSystemService.DirectoryExists(configuration.FileShareUncPath)) return; base.Log.LogError(FileShareStrings.WorkspaceFileShareInvalidExceptionMessage, configuration.FileShareUncPath, LogRedaction.OnPositions(default(int))); throw new TransferException("Workspace file share is not accessible - check logs for more details.", true); }); } } protected override TransferPathResult OnExecute(TransferPath path, CancellationToken token) { if (path == (TransferPath)null) throw new ArgumentNullException("path"); TransferPathResult transferPathResult = new TransferPathResult { Path = path, Status = TransferPathStatus.Started }; if (!token.IsCancellationRequested) try { if (!base.FileSystemService.DirectoryExists(path.TargetPath)) base.FileSystemService.CreateDirectory(path.TargetPath); string destination = (!string.IsNullOrEmpty(path.TargetFileName)) ? base.FileSystemService.Combine(path.TargetPath, path.TargetFileName) : base.FileSystemService.Combine(path.TargetPath, base.FileSystemService.GetFileName(path.SourcePath)); LargeFileProgress progress = (base.LargeFileProgressProvider != null) ? new LargeFileProgress(OnLargeFileProgress, base.LargeFileProgressRateSeconds) : null; Transfer(path, path.SourcePath, destination, transferPathResult, token, progress); return transferPathResult; } catch (PotentialMalwareException e) { IssueAttributes attributes = IssueAttributes.File | IssueAttributes.Warning | IssueAttributes.Malware; PublishTransferIssue(attributes, path, e); return new TransferPathResult { Completed = true, Path = path, Status = TransferPathStatus.FileBlocked }; } catch (OperationCanceledException) { HandleCancel(transferPathResult); return transferPathResult; } catch (UnauthorizedAccessException ex2) { IssueAttributes attributes2 = IssueAttributes.File | IssueAttributes.ReadWritePermissions | (base.FilePermissionErrorsRetry ? GetWarningErrorAttribute() : IssueAttributes.Error); PublishTransferIssue(attributes2, path, ex2); return HandleUnauthorizedAccessException(path, ex2, transferPathResult, configuration.MaxJobRetryAttempts); } catch (ArgumentException ex3) { LogError(ex3.Message, true, ex3); IssueAttributes attributes3 = IssueAttributes.Error | IssueAttributes.File | IssueAttributes.InvalidCharacters; PublishTransferIssue(attributes3, path, ex3); throw new TransferException(ex3.Message, ex3, true); } catch (PathTooLongException ex4) { LogError(ex4.Message, true, ex4); IssueAttributes attributes4 = IssueAttributes.Error | IssueAttributes.File | IssueAttributes.LongPath; PublishTransferIssue(attributes4, path, ex4); throw new TransferException(ex4.Message, ex4, true); } catch (DirectoryNotFoundException ex5) { string errorMessage = FileshareErrorHelper.GetErrorMessage(path, ex5); LogError(errorMessage, false, ex5); IssueAttributes attributes5 = IssueAttributes.DirectoryNotFound | IssueAttributes.Io | GetWarningErrorAttribute(); PublishTransferIssue(attributes5, path, ex5); transferPathResult.Status = TransferPathStatus.Failed; transferPathResult.EndTime = DateTime.Now; return transferPathResult; } catch (FileNotFoundException ex6) { string errorMessage2 = FileshareErrorHelper.GetErrorMessage(path, ex6); if (base.FileNotFoundErrorsDisabled) LogWarning(errorMessage2, false, ex6); else LogError(errorMessage2, false, ex6); IssueAttributes attributes6 = IssueAttributes.File | IssueAttributes.FileNotFound | IssueAttributes.Io | (base.FileNotFoundErrorsDisabled ? IssueAttributes.Warning : GetWarningErrorAttribute()); PublishTransferIssue(attributes6, path, ex6); transferPathResult.Status = TransferPathStatus.FileNotFound; transferPathResult.EndTime = DateTime.Now; return transferPathResult; } catch (IOException ex7) { LogError(ex7.Message, true, ex7); if (FileshareErrorHelper.IsDiskFull(ex7)) { IssueAttributes attributes7 = IssueAttributes.Error | IssueAttributes.Io | IssueAttributes.StorageOutOfSpace; PublishTransferIssue(attributes7, path, ex7); throw new TransferException(ex7.Message, ex7, true); } IssueAttributes attributes8 = IssueAttributes.File | IssueAttributes.Io | IssueAttributes.Overwrite | IssueAttributes.StorageReadWrite | GetWarningErrorAttribute(); PublishTransferIssue(attributes8, path, ex7); throw new TransferException(ex7.Message, ex7, false); } catch (NotSupportedException ex8) { LogError(ex8.Message, true, ex8); IssueAttributes attributes9 = IssueAttributes.Error | IssueAttributes.File | IssueAttributes.InvalidPath; PublishTransferIssue(attributes9, path, ex8); throw new TransferException(ex8.Message, ex8, true); } catch (Exception ex9) { LogError(ex9.Message, true, ex9); IssueAttributes attributes10 = IssueAttributes.Error; PublishTransferIssue(attributes10, path, ex9); throw new TransferException(ex9.Message, ex9, true); } HandleCancel(transferPathResult); return transferPathResult; } private void PublishTransferIssue(IssueAttributes attributes, TransferPath path, Exception e) { ITransferIssue issue = issueBuilderFactory.Create().CreateFromException(path, e).WithAttributes(attributes) .WithMaxRetryAttempts(base.MaxJobRetryAttempts) .WithRetryAttempt(base.JobService.Statistics.RetryAttempt) .Build(); RegisterIssue(issue); } private void Transfer(TransferPath path, string source, string destination, TransferPathResult result, CancellationToken token, IProgress<LargeFileProgressEventArgs> progress) { if (string.IsNullOrEmpty(source)) throw new ArgumentNullException("source"); if (string.IsNullOrEmpty(destination)) throw new ArgumentNullException("source"); if (token.IsCancellationRequested) HandleCancel(result); else { result.StartTime = DateTime.Now; if (base.FileSystemService.FileExists(source)) CopyFile(path, source, destination, result, token, progress); else { if (!base.FileSystemService.DirectoryExists(source)) { base.Log.LogError(FileShareStrings.TransferCommandUnableToFindFile, source, LogRedaction.OnPositions(default(int))); throw new FileNotFoundException(FileShareStrings.TransferCommandFileNotFoundCheckLogs); } if (!CopyDirectories) throw new ArgumentException(FileShareStrings.TransferCommandNotConfiguredToTransferDirectoriesErrorMessage); CopyDirectory(path, source, destination, result, token); } } } private void CopyDirectory(TransferPath path, string sourcePath, string destinationPath, TransferPathResult result, CancellationToken token) { if (string.IsNullOrEmpty(sourcePath)) throw new ArgumentNullException("sourcePath"); if (string.IsNullOrEmpty(destinationPath)) throw new ArgumentNullException("destinationPath"); if (token.IsCancellationRequested) HandleCancel(result); else { if (base.FileSystemService.DirectoryExists(destinationPath)) { if (!OverWriteFilesAtDestination) throw new IOException(FileShareStrings.TargetDirectoryAlreadyExistsExceptionMessage); base.FileSystemService.DeleteDirectory(destinationPath, true); } base.FileSystemService.CreateDirectory(destinationPath); string[] files = base.FileSystemService.GetFiles(sourcePath); foreach (string text in files) { if (token.IsCancellationRequested) { HandleCancel(result); return; } CopyFile(path, text, base.FileSystemService.Combine(destinationPath, base.FileSystemService.GetFileName(text)), result, token, null); } if (CopySubDirectories) { foreach (FolderItem item in base.FileSystemService.EnumerateDirectories(sourcePath)) { if (token.IsCancellationRequested) { HandleCancel(result); return; } CopyDirectory(path, item.FullName, base.FileSystemService.Combine(destinationPath, item.Name), result, token); } } result.EndTime = DateTime.Now; result.Status = (token.IsCancellationRequested ? TransferPathStatus.Canceled : TransferPathStatus.Successful); } } private void CopyFile(TransferPath path, string sourcePath, string destinationPath, TransferPathResult result, CancellationToken token, IProgress<LargeFileProgressEventArgs> progress) { DateTime? sourceCreationTime = null; DateTime? sourceLastAccessTime = null; DateTime? sourceLastWriteTime = null; long num = 0; if (string.IsNullOrEmpty(sourcePath)) throw new ArgumentNullException("sourcePath"); if (string.IsNullOrEmpty(destinationPath)) throw new ArgumentNullException("destinationPath"); if (token.IsCancellationRequested) HandleCancel(result); else { if (base.PreserveDates) { sourceCreationTime = base.FileSystemService.GetFileCreationTime(sourcePath); sourceLastAccessTime = base.FileSystemService.GetFileLastAccessTime(sourcePath); sourceLastWriteTime = base.FileSystemService.GetFileLastWriteTime(sourcePath); } using (FileStream fileStream = CreateFileStreamForSource(sourcePath)) using (FileStream fileStream2 = CreateFileStreamForDestination(destinationPath)) { byte[] buffer = new byte[base.FileSystemChunkSize]; int num2 = 0; long length = fileStream.Length; int totalChunks = TransferHelper.CalculateTotalChunks(length, base.FileSystemChunkSize); int num3; while ((num3 = fileStream.Read(buffer, 0, base.FileSystemChunkSize)) != 0) { num2++; if (token.IsCancellationRequested) { HandleCancel(result); return; } fileStream2.Write(buffer, 0, num3); num += num3; progress?.Report(new LargeFileProgressEventArgs(path, num, length, num2, totalChunks)); } result.EndTime = DateTime.Now; result.Status = (token.IsCancellationRequested ? TransferPathStatus.Canceled : TransferPathStatus.Successful); result.BytesTransferred += num; result.Checksum = string.Empty; } SaveSourceCreationTime(destinationPath, sourceCreationTime); SaveSourceLastAccessTime(destinationPath, sourceLastAccessTime); SaveSourceLastWriteTime(destinationPath, sourceLastWriteTime); if (result.Status == TransferPathStatus.Successful || result.Status == TransferPathStatus.Skipped) base.JobService.Statistics.TotalTransferredFiles++; base.JobService.Statistics.TotalTransferredBytes += num; PublishTransferStatistics(false); } } private FileStream CreateFileStreamForSource(string sourcePath) { try { return new FileStream(PathHelper.CheckAddLongPathPrefix(sourcePath), FileMode.Open, FileAccess.Read, System.IO.FileShare.ReadWrite); } catch (Exception ex) { LogError(ex, "source"); if (ex.ContainsAntiMalwareEvent()) throw new PotentialMalwareException(ex); throw; } } private void LogError(Exception ex, string streamType) { int lastError = NativeMethods.GetLastError(); string message = new Win32Exception(lastError).Message; base.Log.LogError(ex, "An exception of type {ExceptionType} with message '{ExceptionMessage}' was thrown while a {StreamType} file stream was created. Possible error code was {ErrorCode} and error message was '{ErrorMessage}'.", ex.GetType().FullName, ex.Message, streamType, lastError, message); } private FileStream CreateFileStreamForDestination(string destinationPath) { FileMode mode = (!OverWriteFilesAtDestination) ? FileMode.CreateNew : FileMode.Create; try { return new FileStream(PathHelper.CheckAddLongPathPrefix(destinationPath), mode); } catch (Exception ex) { LogError(ex, "destination"); throw; } } private void SaveSourceLastWriteTime(string destinationPath, DateTime? sourceLastWriteTime) { if (sourceLastWriteTime.HasValue) try { base.FileSystemService.SetFileLastWriteTime(destinationPath, sourceLastWriteTime.Value); } catch (Exception exception) { base.Log.LogError(exception, "Failed to preserve the {Path} last write time.", destinationPath, LogRedaction.OnPositions(default(int))); if (ExceptionHelper.IsFatalException(exception)) throw; } } private void SaveSourceLastAccessTime(string destinationPath, DateTime? sourceLastAccessTime) { if (sourceLastAccessTime.HasValue) try { base.FileSystemService.SetFileLastAccessTime(destinationPath, sourceLastAccessTime.Value); } catch (Exception exception) { base.Log.LogError(exception, "Failed to preserve the {Path} last access time.", destinationPath, LogRedaction.OnPositions(default(int))); if (ExceptionHelper.IsFatalException(exception)) throw; } } private void SaveSourceCreationTime(string destinationPath, DateTime? sourceCreationTime) { if (sourceCreationTime.HasValue) try { base.FileSystemService.SetFileCreationTime(destinationPath, sourceCreationTime.Value); } catch (Exception exception) { base.Log.LogError(exception, "Failed to preserve the {Path} creation time.", destinationPath, LogRedaction.OnPositions(default(int))); if (ExceptionHelper.IsFatalException(exception)) throw; } } private long GetDirectorySize(string path) { long num = base.FileSystemService.GetFiles(path).Sum((string file) => base.FileSystemService.GetFileLength(file)); if (!CopySubDirectories) return num; IEnumerable<FolderItem> source = base.FileSystemService.EnumerateDirectories(path); return num + source.Sum((FolderItem dir) => GetDirectorySize(dir.FullName)); } private void LogError(string message, bool useRedaction, Exception exception = null) { if (useRedaction) base.Log.LogTransferError(exception, base.Request, "FileShare Transfer Command - {0}", message, LogRedaction.OnPositions(default(int))); else base.Log.LogTransferError(exception, base.Request, "FileShare Transfer Command - {0}", message); } private void LogWarning(string message, bool useRedaction, Exception exception = null) { if (useRedaction) base.Log.LogTransferWarning(exception, base.Request, "FileShare Transfer Command - {0}", message, LogRedaction.OnPositions(default(int))); else base.Log.LogTransferWarning(exception, base.Request, "FileShare Transfer Command - {0}", message); } } }