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

NativeNodeEnumerator

using Relativity.DataTransfer.Nodes; using Relativity.Transfer.Enumeration.Helpers; using Relativity.Transfer.Enumeration.Interfaces; using Relativity.Transfer.Enumeration.Native; using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Runtime.InteropServices; using System.Threading; namespace Relativity.Transfer.Enumeration { internal sealed class NativeNodeEnumerator : IEnumerator<INode>, IDisposable, IEnumerator { private INode _parentNode; private SafeFindHandle _fileHandle; private NativeFindData _findData; private readonly INodeFactory _nodeFactory; private readonly ITransferLog _logger; private readonly CancellationToken _token; private readonly INativeMethods _nativeMethods; private bool _isEmpty = true; private bool _nodeEnumerated; private bool _selfFound; object IEnumerator.Current { get { return Current; } } public INode Current { get; set; } private DateTime LastModified => SafeGetLastWriteTime(); private bool IsDirectory => NativeMethodHelper.IsDirectory(_findData); private bool IsSymbolicLink => NativeMethodHelper.IsSymbolicLink(_findData); private long Size => NativeMethodHelper.ToSize(_findData); public NativeNodeEnumerator(INode initialNode, INodeFactory nodeFactory, INativeMethods nativeMethods, ITransferLog logger, CancellationToken token) { _nodeFactory = nodeFactory; _nativeMethods = nativeMethods; _logger = logger; _token = token; Current = initialNode; Init(initialNode); } public bool MoveNext() { bool flag; do { if (_token.IsCancellationRequested) throw new OperationCanceledException("NativeNodeEnumerator was requested to abort the enumeration."); if (_nodeEnumerated) return false; if (_fileHandle.IsInvalid) return HandleInvalidNode(); if (!_selfFound) { if (IsDirectory && !IsSymbolicLink) { SetCurrentAsDirectory(false); _isEmpty = false; _selfFound = !_selfFound; return true; } if (!IsDirectory) { SetCurrentAsFile(); _isEmpty = false; _selfFound = !_selfFound; return true; } } flag = FindNextFileWithErrorLogging(); } while (flag && NativeMethodHelper.IsDotOrTwoDotPath(_findData)); if (flag && IsDirectory && !IsSymbolicLink) { SetCurrentAsDirectory(false); _isEmpty = false; return true; } if (flag && !IsDirectory) { SetCurrentAsFile(); _isEmpty = false; return true; } if (_isEmpty) { _nodeEnumerated = true; SetCurrentAsSelfDirectory(true); return true; } return false; } private bool FindNextFileWithErrorLogging() { bool num = _nativeMethods.FindNextFile(_fileHandle, _findData); if (!num) LogLastWin32ErrorCode(); return num; } private void LogLastWin32ErrorCode() { int lastWin32Error = Marshal.GetLastWin32Error(); if (!IsEnumerationCompletedWithoutError(lastWin32Error)) _logger.LogInformation(new Win32Exception(lastWin32Error), "Error when calling FindNextFile {0}", lastWin32Error); } private static bool IsEnumerationCompletedWithoutError(int lastWin32Error) { if (lastWin32Error != 0) return lastWin32Error == 18; return true; } private bool HandleInvalidNode() { int lastWin32Error = Marshal.GetLastWin32Error(); string text = Current?.AbsolutePath; if (NativeMethodHelper.FileExists(text)) { SetCurrentAsSelfFile(); _nodeEnumerated = true; _logger.LogInformation("Enumeration yielded {0}. Returning it as file (even though file handle is invalid).", text, LogRedaction.OnPositions(default(int))); return true; } if (NativeMethodHelper.DirectoryExists(text)) { SetCurrentAsSelfDirectory(true); _nodeEnumerated = true; _logger.LogInformation("Enumeration yielded {0}. Returning it as directory (even though file handle is invalid).", text, LogRedaction.OnPositions(default(int))); return true; } return AbortEnumeration(lastWin32Error); } public void Reset() { _findData = new NativeFindData(); Current = _parentNode; FindFile(_parentNode); } public void Dispose() { _fileHandle?.Dispose(); } private void Init(INode initialNode) { _parentNode = initialNode; Reset(); } private void FindFile(INode node) { _fileHandle?.Dispose(); string fileName = LongPathHelper.ToNativeFormat(node.AbsolutePath); _fileHandle = _nativeMethods.FindFirstFile(fileName, _findData); if (NativeMethodHelper.IsDotOrTwoDotPath(_findData)) _selfFound = true; } private void SetCurrentAsFile() { Current = _nodeFactory.CreateFile(_findData.cFileName, _parentNode, LastModified, Size); } private void SetCurrentAsSelfFile() { IFile file = Current as IFile; Current = _nodeFactory.CreateFile(file.Name, file.Parent, file.Modified, file.Size); } private void SetCurrentAsDirectory(bool isEmpty) { Current = _nodeFactory.CreateDirectory(_findData.cFileName, _parentNode, LastModified, isEmpty); } private void SetCurrentAsSelfDirectory(bool isEmpty) { IDirectory directoryNode = Current as IDirectory; Current = _nodeFactory.CreateDirectory(directoryNode, isEmpty); } private static bool AbortEnumeration(int lastErrorCode) { if (lastErrorCode == 3) throw new PathDoesNotExistException(); throw new EnumerationException(lastErrorCode); } private DateTime SafeGetLastWriteTime() { try { return NativeMethodHelper.ToDateTime(_findData); } catch (ArgumentOutOfRangeException exception) { long num = NativeMethodHelper.ToFileTime(_findData); _logger.LogWarning(exception, "Unsupported file time occurred [ftLastWriteTime_dwHighDateTime={0}, ftLastWriteTime_dwLowDateTime={1}, fileTime={2}].", _findData.ftLastWriteTime_dwHighDateTime, _findData.ftLastWriteTime_dwLowDateTime, num); return DateTime.MinValue; } } } }