# < Summary

Information

Class: | Renci.SshNet.Sftp.SftpSession
Assembly: | Renci.SshNet
File(s): | \home\appveyor\projects\ssh-net\src\Renci.SshNet\Sftp\SftpSession.cs

Line coverage
64%

Covered lines: | 805
Uncovered lines: | 441
Coverable lines: | 1246
Total lines: | 2313
Line coverage: | 64.6%

Branch coverage
50%

Covered branches: | 116
Total branches: | 231
Branch coverage: | 50.2%

Method coverage

Feature is only available for sponsors

Upgrade to PRO version

# Metrics

Method | Branch coverage | Cyclomatic complexity | Line coverage
--- | --- | --- | ---
.ctor(...) | 100% | 1 | 100%
get_WorkingDirectory() | 100% | 1 | 100%
get_ProtocolVersion() | 100% | 1 | 100%
get_NextRequestId() | 100% | 1 | 100%
ChangeDirectory(...) | 100% | 1 | 100%
SendMessage(...) | 100% | 1 | 100%
GetCanonicalPath(...) | 75% | 20 | 87.17%
GetCanonicalPathAsync() | 15% | 20 | 30.76%
CreateFileReader(...) | 100% | 1 | 100%
GetFullRemotePath(...) | 87.5% | 8 | 78.57%
OnChannelOpen() | 50% | 2 | 75%
OnDataReceived(...) | 90% | 20 | 92.85%
TryLoadSftpMessage(...) | 100% | 2 | 78.94%
Dispose(...) | 100% | 4 | 100%
SendRequest(...) | 100% | 1 | 100%
RequestOpen(...) | 100% | 4 | 100%
RequestOpenAsync() | 100% | 1 | 100%
BeginOpen(...) | 100% | 1 | 100%
EndOpen(...) | 50% | 6 | 60%
RequestClose(...) | 50% | 2 | 89.47%
RequestCloseAsync() | 100% | 1 | 100%
BeginClose(...) | 100% | 1 | 100%
EndClose(...) | 50% | 6 | 63.15%
BeginRead(...) | 50% | 2 | 88%
EndRead(...) | 50% | 6 | 60%
RequestRead(...) | 50% | 4 | 86.11%
RequestReadAsync() | 100% | 1 | 0%
RequestWrite(...) | 87.5% | 8 | 92.85%
RequestWriteAsync() | 100% | 1 | 100%
RequestLStat(...) | 100% | 2 | 100%
BeginLStat(...) | 100% | 1 | 88.23%
EndLStat(...) | 66.66% | 6 | 73.33%
RequestFStat(...) | 75% | 4 | 80.76%
RequestFStatAsync() | 100% | 1 | 0%
RequestSetStat(...) | 50% | 2 | 90.47%
RequestFSetStat(...) | 50% | 2 | 90%
RequestOpenDir(...) | 75% | 4 | 100%
RequestOpenDirAsync() | 100% | 1 | 100%
RequestReadDir(...) | 50% | 4 | 83.33%
RequestReadDirAsync() | 100% | 1 | 100%
RequestRemove(...) | 50% | 2 | 90%
RequestRemoveAsync() | 100% | 1 | 0%
RequestMkDir(...) | 100% | 2 | 100%
RequestRmDir(...) | 100% | 2 | 100%
RequestRealPath(...) | 75% | 4 | 92.59%
RequestRealPathAsync() | 100% | 1 | 100%
BeginRealPath(...) | 100% | 1 | 0%
EndRealPath(...) | 0% | 6 | 0%
RequestStat(...) | 0% | 4 | 0%
BeginStat(...) | 100% | 1 | 0%
EndStat(...) | 0% | 6 | 0%
RequestRename(...) | 50% | 4 | 83.33%
RequestRenameAsync() | 100% | 1 | 100%
RequestReadLink(...) | 0% | 6 | 0%
RequestSymLink(...) | 0% | 4 | 0%
RequestPosixRename(...) | 0% | 6 | 0%
RequestStatVfs(...) | 50% | 8 | 72.72%
RequestStatVfsAsync() | 0% | 2 | 0%
RequestFStatVfs(...) | 0% | 8 | 0%
HardLink(...) | 0% | 6 | 0%
CalculateOptimalReadLength(...) | 100% | 1 | 100%
CalculateOptimalWriteLength(...) | 100% | 1 | 100%
GetSftpException(...) | 100% | 5 | 100%
HandleResponse(...) | 75% | 4 | 85.71%

# File(s)

## \home\appveyor\projects\ssh-net\src\Renci.SshNet\Sftp\SftpSession.cs

| # | Line |  | Line coverage
--- | --- | --- | --- | ---
|  | `1` |  | `using System;`
|  | `2` |  | `using System.Collections.Generic;`
|  | `3` |  | `using System.Globalization;`
|  | `4` |  | `using System.Text;`
|  | `5` |  | `using System.Threading;`
|  | `6` |  | `using System.Threading.Tasks;`
|  | `7` |  | ``
|  | `8` |  | `using Renci.SshNet.Common;`
|  | `9` |  | `using Renci.SshNet.Sftp.Requests;`
|  | `10` |  | `using Renci.SshNet.Sftp.Responses;`
|  | `11` |  | ``
|  | `12` |  | `namespace Renci.SshNet.Sftp`
|  | `13` |  | `{`
|  | `14` |  | `/// <summary>`
|  | `15` |  | `/// Represents an SFTP session.`
|  | `16` |  | `/// </summary>`
|  | `17` |  | `internal sealed class SftpSession : SubsystemSession, ISftpSession`
|  | `18` |  | `{`
|  | `19` |  | `internal const int MaximumSupportedVersion = 3;`
|  | `20` |  | `private const int MinimumSupportedVersion = 0;`
|  | `21` |  | ``
| 642 | `22` |  | `private readonly Dictionary<uint, SftpRequest> _requests = new Dictionary<uint, SftpRequest>();`
|  | `23` |  | `private readonly ISftpResponseFactory _sftpResponseFactory;`
| 642 | `24` |  | `private readonly List<byte> _data = new List<byte>(32 * 1024);`
|  | `25` |  | `private readonly Encoding _encoding;`
| 642 | `26` |  | `private EventWaitHandle _sftpVersionConfirmed = new AutoResetEvent(initialState: false);`
|  | `27` |  | `private IDictionary<string, string> _supportedExtensions;`
|  | `28` |  | ``
|  | `29` |  | `/// <summary>`
|  | `30` |  | `/// Gets the remote working directory.`
|  | `31` |  | `/// </summary>`
|  | `32` |  | `/// <value>`
|  | `33` |  | `/// The remote working directory.`
|  | `34` |  | `/// </value>`
| 41556 | `35` |  | `public string WorkingDirectory { get; private set; }`
|  | `36` |  | ``
|  | `37` |  | `/// <summary>`
|  | `38` |  | `/// Gets the SFTP protocol version.`
|  | `39` |  | `/// </summary>`
|  | `40` |  | `/// <value>`
|  | `41` |  | `/// The SFTP protocol version.`
|  | `42` |  | `/// </value>`
| 64970 | `43` |  | `public uint ProtocolVersion { get; private set; }`
|  | `44` |  | ``
|  | `45` |  | `private long _requestId;`
|  | `46` |  | ``
|  | `47` |  | `/// <summary>`
|  | `48` |  | `/// Gets the next request id for sftp session.`
|  | `49` |  | `/// </summary>`
|  | `50` |  | `public uint NextRequestId`
|  | `51` |  | `{`
|  | `52` |  | `get`
| 31521 | `53` |  | `{`
| 31521 | `54` |  | `return (uint) Interlocked.Increment(ref _requestId);`
| 31521 | `55` |  | `}`
|  | `56` |  | `}`
|  | `57` |  | ``
|  | `58` |  | `/// <summary>`
|  | `59` |  | `/// Initializes a new instance of the <see cref="SftpSession"/> class.`
|  | `60` |  | `/// </summary>`
|  | `61` |  | `/// <param name="session">The SSH session.</param>`
|  | `62` |  | `/// <param name="operationTimeout">The operation timeout.</param>`
|  | `63` |  | `/// <param name="encoding">The character encoding to use.</param>`
|  | `64` |  | `/// <param name="sftpResponseFactory">The factory to create SFTP responses.</param>`
|  | `65` |  | `public SftpSession(ISession session, int operationTimeout, Encoding encoding, ISftpResponseFactory sftpResponseF`
| 642 | `66` |  | `: base(session, "sftp", operationTimeout)`
| 642 | `67` |  | `{`
| 642 | `68` |  | `_encoding = encoding;`
| 642 | `69` |  | `_sftpResponseFactory = sftpResponseFactory;`
| 642 | `70` |  | `}`
|  | `71` |  | ``
|  | `72` |  | `/// <summary>`
|  | `73` |  | `/// Changes the current working directory to the specified path.`
|  | `74` |  | `/// </summary>`
|  | `75` |  | `/// <param name="path">The new working directory.</param>`
|  | `76` |  | `public void ChangeDirectory(string path)`
| 20 | `77` |  | `{`
| 20 | `78` |  | `var fullPath = GetCanonicalPath(path);`
| 20 | `79` |  | `var handle = RequestOpenDir(fullPath);`
|  | `80` |  | ``
| 15 | `81` |  | `RequestClose(handle);`
| 15 | `82` |  | `WorkingDirectory = fullPath;`
| 15 | `83` |  | `}`
|  | `84` |  | ``
|  | `85` |  | `internal void SendMessage(SftpMessage sftpMessage)`
| 32162 | `86` |  | `{`
| 32162 | `87` |  | `var data = sftpMessage.GetBytes();`
| 32162 | `88` |  | `SendData(data);`
| 32160 | `89` |  | `}`
|  | `90` |  | ``
|  | `91` |  | `/// <summary>`
|  | `92` |  | `/// Resolves a given path into an absolute path on the server.`
|  | `93` |  | `/// </summary>`
|  | `94` |  | `/// <param name="path">The path to resolve.</param>`
|  | `95` |  | `/// <returns>`
|  | `96` |  | `/// The absolute path.`
|  | `97` |  | `/// </returns>`
|  | `98` |  | `public string GetCanonicalPath(string path)`
| 10885 | `99` |  | `{`
| 10885 | `100` |  | `var fullPath = GetFullRemotePath(path);`
|  | `101` |  | ``
| 10885 | `102` |  | `var canonizedPath = string.Empty;`
|  | `103` |  | ``
| 10885 | `104` |  | `var realPathFiles = RequestRealPath(fullPath, nullOnError: true);`
|  | `105` |  | ``
| 10882 | `106` |  | `if (realPathFiles != null)`
| 10793 | `107` |  | `{`
| 10793 | `108` |  | `canonizedPath = realPathFiles[0].Key;`
| 10793 | `109` |  | `}`
|  | `110` |  | ``
| 10882 | `111` |  | `if (!string.IsNullOrEmpty(canonizedPath))`
| 10793 | `112` |  | `{`
| 10793 | `113` |  | `return canonizedPath;`
|  | `114` |  | `}`
|  | `115` |  | ``
|  | `116` |  | `// Check for special cases`
| 89 | `117` |  | `if (fullPath.EndsWith("/.", StringComparison.OrdinalIgnoreCase) ||`
| 89 | `118` |  | `fullPath.EndsWith("/..", StringComparison.OrdinalIgnoreCase) ||`
| 89 | `119` |  | `fullPath.Equals("/", StringComparison.OrdinalIgnoreCase) ||`
| 89 | `120` |  | `#if NET || NETSTANDARD2_1_OR_GREATER`
| 89 | `121` |  | `fullPath.IndexOf('/', StringComparison.OrdinalIgnoreCase) < 0)`
| 0 | `122` |  | `#else`
| 0 | `123` |  | `fullPath.IndexOf('/') < 0)`
|  | `124` |  | `#endif // NET || NETSTANDARD2_1_OR_GREATER`
| 0 | `125` |  | `{`
| 0 | `126` |  | `return fullPath;`
|  | `127` |  | `}`
|  | `128` |  | ``
| 89 | `129` |  | `var pathParts = fullPath.Split('/');`
|  | `130` |  | ``
|  | `131` |  | `#if NET || NETSTANDARD2_1_OR_GREATER`
| 89 | `132` |  | `var partialFullPath = string.Join('/', pathParts, 0, pathParts.Length - 1);`
|  | `133` |  | `#else`
| 0 | `134` |  | `var partialFullPath = string.Join("/", pathParts, 0, pathParts.Length - 1);`
|  | `135` |  | `#endif // NET || NETSTANDARD2_1_OR_GREATER`
|  | `136` |  | ``
| 89 | `137` |  | `if (string.IsNullOrEmpty(partialFullPath))`
| 0 | `138` |  | `{`
| 0 | `139` |  | `partialFullPath = "/";`
| 0 | `140` |  | `}`
|  | `141` |  | ``
| 89 | `142` |  | `realPathFiles = RequestRealPath(partialFullPath, nullOnError: true);`
|  | `143` |  | ``
| 89 | `144` |  | `if (realPathFiles != null)`
| 81 | `145` |  | `{`
| 81 | `146` |  | `canonizedPath = realPathFiles[0].Key;`
| 81 | `147` |  | `}`
|  | `148` |  | ``
| 89 | `149` |  | `if (string.IsNullOrEmpty(canonizedPath))`
| 8 | `150` |  | `{`
| 8 | `151` |  | `return fullPath;`
|  | `152` |  | `}`
|  | `153` |  | ``
| 81 | `154` |  | `var slash = string.Empty;`
| 81 | `155` |  | `if (canonizedPath[canonizedPath.Length - 1] != '/')`
| 81 | `156` |  | `{`
| 81 | `157` |  | `slash = "/";`
| 81 | `158` |  | `}`
|  | `159` |  | ``
| 81 | `160` |  | `return string.Format(CultureInfo.InvariantCulture, "{0}{1}{2}", canonizedPath, slash, pathParts[pathParts.Le`
| 10882 | `161` |  | `}`
|  | `162` |  | ``
|  | `163` |  | `/// <summary>`
|  | `164` |  | `/// Asynchronously resolves a given path into an absolute path on the server.`
|  | `165` |  | `/// </summary>`
|  | `166` |  | `/// <param name="path">The path to resolve.</param>`
|  | `167` |  | `/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>`
|  | `168` |  | `/// <returns>`
|  | `169` |  | `/// A task representing the absolute path.`
|  | `170` |  | `/// </returns>`
|  | `171` |  | `public async Task<string> GetCanonicalPathAsync(string path, CancellationToken cancellationToken)`
| 4 | `172` |  | `{`
| 4 | `173` |  | `var fullPath = GetFullRemotePath(path);`
|  | `174` |  | ``
| 4 | `175` |  | `var canonizedPath = string.Empty;`
| 4 | `176` |  | `var realPathFiles = await RequestRealPathAsync(fullPath, nullOnError: true, cancellationToken).ConfigureAwai`
| 4 | `177` |  | `if (realPathFiles != null)`
| 4 | `178` |  | `{`
| 4 | `179` |  | `canonizedPath = realPathFiles[0].Key;`
| 4 | `180` |  | `}`
|  | `181` |  | ``
| 4 | `182` |  | `if (!string.IsNullOrEmpty(canonizedPath))`
| 4 | `183` |  | `{`
| 4 | `184` |  | `return canonizedPath;`
|  | `185` |  | `}`
|  | `186` |  | ``
|  | `187` |  | `// Check for special cases`
| 0 | `188` |  | `if (fullPath.EndsWith("/.", StringComparison.Ordinal) ||`
| 0 | `189` |  | `fullPath.EndsWith("/..", StringComparison.Ordinal) ||`
| 0 | `190` |  | `fullPath.Equals("/", StringComparison.Ordinal) ||`
| 0 | `191` |  | `#if NET || NETSTANDARD2_1_OR_GREATER`
| 0 | `192` |  | `fullPath.IndexOf('/', StringComparison.Ordinal) < 0)`
| 0 | `193` |  | `#else`
| 0 | `194` |  | `fullPath.IndexOf('/') < 0)`
|  | `195` |  | `#endif // NET || NETSTANDARD2_1_OR_GREATER`
| 0 | `196` |  | `{`
| 0 | `197` |  | `return fullPath;`
|  | `198` |  | `}`
|  | `199` |  | ``
| 0 | `200` |  | `var pathParts = fullPath.Split('/');`
|  | `201` |  | ``
|  | `202` |  | `#if NET || NETSTANDARD2_1_OR_GREATER`
| 0 | `203` |  | `var partialFullPath = string.Join('/', pathParts);`
|  | `204` |  | `#else`
| 0 | `205` |  | `var partialFullPath = string.Join("/", pathParts);`
|  | `206` |  | `#endif // NET || NETSTANDARD2_1_OR_GREATER`
|  | `207` |  | ``
| 0 | `208` |  | `if (string.IsNullOrEmpty(partialFullPath))`
| 0 | `209` |  | `{`
| 0 | `210` |  | `partialFullPath = "/";`
| 0 | `211` |  | `}`
|  | `212` |  | ``
| 0 | `213` |  | `realPathFiles = await RequestRealPathAsync(partialFullPath, nullOnError: true, cancellationToken).ConfigureA`
|  | `214` |  | ``
| 0 | `215` |  | `if (realPathFiles != null)`
| 0 | `216` |  | `{`
| 0 | `217` |  | `canonizedPath = realPathFiles[0].Key;`
| 0 | `218` |  | `}`
|  | `219` |  | ``
| 0 | `220` |  | `if (string.IsNullOrEmpty(canonizedPath))`
| 0 | `221` |  | `{`
| 0 | `222` |  | `return fullPath;`
|  | `223` |  | `}`
|  | `224` |  | ``
| 0 | `225` |  | `var slash = string.Empty;`
| 0 | `226` |  | `if (canonizedPath[canonizedPath.Length - 1] != '/')`
| 0 | `227` |  | `{`
| 0 | `228` |  | `slash = "/";`
| 0 | `229` |  | `}`
|  | `230` |  | ``
| 0 | `231` |  | `return canonizedPath + slash + pathParts[pathParts.Length - 1];`
| 4 | `232` |  | `}`
|  | `233` |  | ``
|  | `234` |  | `/// <summary>`
|  | `235` |  | `/// Creates an <see cref="ISftpFileReader"/> for reading the content of the file represented by a given <paramre`
|  | `236` |  | `/// </summary>`
|  | `237` |  | `/// <param name="handle">The handle of the file to read.</param>`
|  | `238` |  | `/// <param name="sftpSession">The SFTP session.</param>`
|  | `239` |  | `/// <param name="chunkSize">The maximum number of bytes to read with each chunk.</param>`
|  | `240` |  | `/// <param name="maxPendingReads">The maximum number of pending reads.</param>`
|  | `241` |  | `/// <param name="fileSize">The size of the file or <see langword="null"/> when the size could not be determined.`
|  | `242` |  | `/// <returns>`
|  | `243` |  | `/// An <see cref="ISftpFileReader"/> for reading the content of the file represented by the`
|  | `244` |  | `/// specified <paramref name="handle"/>.`
|  | `245` |  | `/// </returns>`
|  | `246` |  | `public ISftpFileReader CreateFileReader(byte[] handle, ISftpSession sftpSession, uint chunkSize, int maxPendingR`
| 46 | `247` |  | `{`
| 46 | `248` |  | `return new SftpFileReader(handle, sftpSession, chunkSize, maxPendingReads, fileSize);`
| 46 | `249` |  | `}`
|  | `250` |  | ``
|  | `251` |  | `internal string GetFullRemotePath(string path)`
| 10889 | `252` |  | `{`
| 10889 | `253` |  | `var fullPath = path;`
|  | `254` |  | ``
| 10889 | `255` |  | `if (!string.IsNullOrEmpty(path) && path[0] != '/' && WorkingDirectory != null)`
| 10205 | `256` |  | `{`
| 10205 | `257` |  | `if (WorkingDirectory[WorkingDirectory.Length - 1] == '/')`
| 0 | `258` |  | `{`
| 0 | `259` |  | `fullPath = WorkingDirectory + path;`
| 0 | `260` |  | `}`
|  | `261` |  | `else`
| 10205 | `262` |  | `{`
| 10205 | `263` |  | `fullPath = WorkingDirectory + '/' + path;`
| 10205 | `264` |  | `}`
| 10205 | `265` |  | `}`
|  | `266` |  | ``
| 10889 | `267` |  | `return fullPath;`
| 10889 | `268` |  | `}`
|  | `269` |  | ``
|  | `270` |  | `protected override void OnChannelOpen()`
| 641 | `271` |  | `{`
| 641 | `272` |  | `SendMessage(new SftpInitRequest(MaximumSupportedVersion));`
|  | `273` |  | ``
| 641 | `274` |  | `WaitOnHandle(_sftpVersionConfirmed, OperationTimeout);`
|  | `275` |  | ``
| 641 | `276` |  | `if (ProtocolVersion is > MaximumSupportedVersion or < MinimumSupportedVersion)`
| 0 | `277` |  | `{`
| 0 | `278` |  | `throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Server SFTP version {0} is no`
|  | `279` |  | `}`
|  | `280` |  | ``
|  | `281` |  | `// Resolve current directory`
| 641 | `282` |  | `WorkingDirectory = RequestRealPath(".")[0].Key;`
| 641 | `283` |  | `}`
|  | `284` |  | ``
|  | `285` |  | `protected override void OnDataReceived(byte[] data)`
| 31887 | `286` |  | `{`
|  | `287` |  | `const int packetLengthByteCount = 4;`
|  | `288` |  | `const int sftpMessageTypeByteCount = 1;`
|  | `289` |  | `const int minimumChannelDataLength = packetLengthByteCount + sftpMessageTypeByteCount;`
|  | `290` |  | ``
| 31887 | `291` |  | `var offset = 0;`
| 31887 | `292` |  | `var count = data.Length;`
|  | `293` |  | ``
|  | `294` |  | `// improve performance and reduce GC pressure by not buffering channel data if the received`
|  | `295` |  | `// chunk contains the complete packet data.`
|  | `296` |  | `//`
|  | `297` |  | `// for this, the buffer should be empty and the chunk should contain at least the packet length`
|  | `298` |  | `// and the type of the SFTP message`
| 31887 | `299` |  | `if (_data.Count == 0)`
| 31492 | `300` |  | `{`
| 63203 | `301` |  | `while (count >= minimumChannelDataLength)`
| 31729 | `302` |  | `{`
|  | `303` |  | `// extract packet length`
| 31729 | `304` |  | `var packetDataLength = data[offset] << 24 | data[offset + 1] << 16 | data[offset + 2] << 8 |`
| 31729 | `305` |  | `data[offset + 3];`
|  | `306` |  | ``
| 31729 | `307` |  | `var packetTotalLength = packetDataLength + packetLengthByteCount;`
|  | `308` |  | ``
|  | `309` |  | `// check if complete packet data (or more) is available`
| 31729 | `310` |  | `if (count >= packetTotalLength)`
| 31711 | `311` |  | `{`
|  | `312` |  | `// load and process SFTP message`
| 31711 | `313` |  | `if (!TryLoadSftpMessage(data, offset + packetLengthByteCount, packetDataLength))`
| 0 | `314` |  | `{`
| 0 | `315` |  | `return;`
|  | `316` |  | `}`
|  | `317` |  | ``
|  | `318` |  | `// remove processed bytes from the number of bytes to process as the channel`
|  | `319` |  | `// data we received may contain (part of) another message`
| 31711 | `320` |  | `count -= packetTotalLength;`
|  | `321` |  | ``
|  | `322` |  | `// move offset beyond bytes we just processed`
| 31711 | `323` |  | `offset += packetTotalLength;`
| 31711 | `324` |  | `}`
|  | `325` |  | `else`
| 18 | `326` |  | `{`
|  | `327` |  | `// we don't have a complete message`
| 18 | `328` |  | `break;`
|  | `329` |  | `}`
| 31711 | `330` |  | `}`
|  | `331` |  | ``
|  | `332` |  | `// check if there is channel data left to process or buffer`
| 31492 | `333` |  | `if (count == 0)`
| 31474 | `334` |  | `{`
| 31474 | `335` |  | `return;`
|  | `336` |  | `}`
|  | `337` |  | ``
|  | `338` |  | `// check if we processed part of the channel data we received`
| 18 | `339` |  | `if (offset > 0)`
| 10 | `340` |  | `{`
|  | `341` |  | `// add (remaining) channel data to internal data holder`
| 10 | `342` |  | `var remainingChannelData = new byte[count];`
| 10 | `343` |  | `Buffer.BlockCopy(data, offset, remainingChannelData, 0, count);`
| 10 | `344` |  | `_data.AddRange(remainingChannelData);`
| 10 | `345` |  | `}`
|  | `346` |  | `else`
| 8 | `347` |  | `{`
|  | `348` |  | `// add (remaining) channel data to internal data holder`
| 8 | `349` |  | `_data.AddRange(data);`
| 8 | `350` |  | `}`
|  | `351` |  | ``
|  | `352` |  | `// skip further processing as we'll need a new chunk to complete the message`
| 18 | `353` |  | `return;`
|  | `354` |  | `}`
|  | `355` |  | ``
|  | `356` |  | `// add (remaining) channel data to internal data holder`
| 395 | `357` |  | `_data.AddRange(data);`
|  | `358` |  | ``
| 843 | `359` |  | `while (_data.Count >= minimumChannelDataLength)`
| 825 | `360` |  | `{`
|  | `361` |  | `// extract packet length`
| 825 | `362` |  | `var packetDataLength = _data[0] << 24 | _data[1] << 16 | _data[2] << 8 | _data[3];`
|  | `363` |  | ``
| 825 | `364` |  | `var packetTotalLength = packetDataLength + packetLengthByteCount;`
|  | `365` |  | ``
|  | `366` |  | `// check if complete packet data is available`
| 825 | `367` |  | `if (_data.Count < packetTotalLength)`
| 377 | `368` |  | `{`
|  | `369` |  | `// wait for complete message to arrive first`
| 377 | `370` |  | `break;`
|  | `371` |  | `}`
|  | `372` |  | ``
|  | `373` |  | `// create buffer to hold packet data`
| 448 | `374` |  | `var packetData = new byte[packetDataLength];`
|  | `375` |  | ``
|  | `376` |  | `// copy packet data and bytes for length to array`
| 448 | `377` |  | `_data.CopyTo(packetLengthByteCount, packetData, 0, packetDataLength);`
|  | `378` |  | ``
|  | `379` |  | `// remove loaded data and bytes for length from _data holder`
| 448 | `380` |  | `if (_data.Count == packetTotalLength)`
| 18 | `381` |  | `{`
|  | `382` |  | `// the only buffered data is the data we're processing`
| 18 | `383` |  | `_data.Clear();`
| 18 | `384` |  | `}`
|  | `385` |  | `else`
| 430 | `386` |  | `{`
|  | `387` |  | `// remove only the data we're processing`
| 430 | `388` |  | `_data.RemoveRange(0, packetTotalLength);`
| 430 | `389` |  | `}`
|  | `390` |  | ``
|  | `391` |  | `// load and process SFTP message`
| 448 | `392` |  | `if (!TryLoadSftpMessage(packetData, 0, packetDataLength))`
| 0 | `393` |  | `{`
| 0 | `394` |  | `break;`
|  | `395` |  | `}`
| 448 | `396` |  | `}`
| 31887 | `397` |  | `}`
|  | `398` |  | ``
|  | `399` |  | `private bool TryLoadSftpMessage(byte[] packetData, int offset, int count)`
| 32159 | `400` |  | `{`
|  | `401` |  | `// Create SFTP message`
| 32159 | `402` |  | `var response = _sftpResponseFactory.Create(ProtocolVersion, packetData[offset], _encoding);`
|  | `403` |  | ``
|  | `404` |  | `// Load message data into it`
| 32159 | `405` |  | `response.Load(packetData, offset + 1, count - 1);`
|  | `406` |  | ``
|  | `407` |  | `try`
| 32159 | `408` |  | `{`
| 32159 | `409` |  | `if (response is SftpVersionResponse versionResponse)`
| 641 | `410` |  | `{`
| 641 | `411` |  | `ProtocolVersion = versionResponse.Version;`
| 641 | `412` |  | `_supportedExtensions = versionResponse.Extentions;`
|  | `413` |  | ``
| 641 | `414` |  | `_ = _sftpVersionConfirmed.Set();`
| 641 | `415` |  | `}`
|  | `416` |  | `else`
| 31518 | `417` |  | `{`
| 31518 | `418` |  | `HandleResponse(response as SftpResponse);`
| 31518 | `419` |  | `}`
|  | `420` |  | ``
| 32159 | `421` |  | `return true;`
|  | `422` |  | `}`
| 0 | `423` |  | `catch (Exception exp)`
| 0 | `424` |  | `{`
| 0 | `425` |  | `RaiseError(exp);`
| 0 | `426` |  | `return false;`
|  | `427` |  | `}`
| 32159 | `428` |  | `}`
|  | `429` |  | ``
|  | `430` |  | `protected override void Dispose(bool disposing)`
| 642 | `431` |  | `{`
| 642 | `432` |  | `base.Dispose(disposing);`
|  | `433` |  | ``
| 642 | `434` |  | `if (disposing)`
| 618 | `435` |  | `{`
| 618 | `436` |  | `var sftpVersionConfirmed = _sftpVersionConfirmed;`
| 618 | `437` |  | `if (sftpVersionConfirmed != null)`
| 618 | `438` |  | `{`
| 618 | `439` |  | `_sftpVersionConfirmed = null;`
| 618 | `440` |  | `sftpVersionConfirmed.Dispose();`
| 618 | `441` |  | `}`
| 618 | `442` |  | `}`
| 642 | `443` |  | `}`
|  | `444` |  | ``
|  | `445` |  | `private void SendRequest(SftpRequest request)`
| 31521 | `446` |  | `{`
| 31521 | `447` |  | `lock (_requests)`
| 31521 | `448` |  | `{`
| 31521 | `449` |  | `_requests.Add(request.RequestId, request);`
| 31521 | `450` |  | `}`
|  | `451` |  | ``
| 31521 | `452` |  | `SendMessage(request);`
| 31519 | `453` |  | `}`
|  | `454` |  | ``
|  | `455` |  | `/// <summary>`
|  | `456` |  | `/// Performs SSH_FXP_OPEN request.`
|  | `457` |  | `/// </summary>`
|  | `458` |  | `/// <param name="path">The path.</param>`
|  | `459` |  | `/// <param name="flags">The flags.</param>`
|  | `460` |  | `/// <param name="nullOnError">If set to <see langword="true"/> returns <see langword="null"/> instead of throwin`
|  | `461` |  | `/// <returns>File handle.</returns>`
|  | `462` |  | `public byte[] RequestOpen(string path, Flags flags, bool nullOnError = false)`
| 354 | `463` |  | `{`
| 354 | `464` |  | `byte[] handle = null;`
| 354 | `465` |  | `SshException exception = null;`
|  | `466` |  | ``
| 354 | `467` |  | `using (var wait = new AutoResetEvent(initialState: false))`
| 354 | `468` |  | `{`
| 354 | `469` |  | `var request = new SftpOpenRequest(ProtocolVersion,`
| 354 | `470` |  | `NextRequestId,`
| 354 | `471` |  | `path,`
| 354 | `472` |  | `_encoding,`
| 354 | `473` |  | `flags,`
| 354 | `474` |  | `response =>`
| 314 | `475` |  | `{`
| 314 | `476` |  | `handle = response.Handle;`
| 314 | `477` |  | `_ = wait.Set();`
| 314 | `478` |  | `},`
| 354 | `479` |  | `response =>`
| 40 | `480` |  | `{`
| 40 | `481` |  | `exception = GetSftpException(response);`
| 40 | `482` |  | `_ = wait.Set();`
| 394 | `483` |  | `});`
|  | `484` |  | ``
| 354 | `485` |  | `SendRequest(request);`
|  | `486` |  | ``
| 354 | `487` |  | `WaitOnHandle(wait, OperationTimeout);`
| 354 | `488` |  | `}`
|  | `489` |  | ``
|  | `490` |  | `#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 354 | `491` |  | `if (!nullOnError && exception is not null)`
|  | `492` |  | `#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 31 | `493` |  | `{`
| 31 | `494` |  | `throw exception;`
|  | `495` |  | `}`
|  | `496` |  | `#pragma warning restore CA1508 // Avoid dead conditional code`
|  | `497` |  | ``
| 323 | `498` |  | `return handle;`
| 323 | `499` |  | `}`
|  | `500` |  | ``
|  | `501` |  | `/// <summary>`
|  | `502` |  | `/// Asynchronously performs a <c>SSH_FXP_OPEN</c> request.`
|  | `503` |  | `/// </summary>`
|  | `504` |  | `/// <param name="path">The path.</param>`
|  | `505` |  | `/// <param name="flags">The flags.</param>`
|  | `506` |  | `/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>`
|  | `507` |  | `/// <returns>`
|  | `508` |  | `/// A task that represents the asynchronous <c>SSH_FXP_OPEN</c> request. The value of its`
|  | `509` |  | `/// <see cref="Task{Task}.Result"/> contains the file handle of the specified path.`
|  | `510` |  | `/// </returns>`
|  | `511` |  | `public async Task<byte[]> RequestOpenAsync(string path, Flags flags, CancellationToken cancellationToken)`
| 1 | `512` |  | `{`
| 1 | `513` |  | `cancellationToken.ThrowIfCancellationRequested();`
|  | `514` |  | ``
| 1 | `515` |  | `var tcs = new TaskCompletionSource<byte[]>(TaskCreationOptions.RunContinuationsAsynchronously);`
|  | `516` |  | ``
|  | `517` |  | `#if NET || NETSTANDARD2_1_OR_GREATER`
| 1 | `518` |  | `await using (cancellationToken.Register(s => ((TaskCompletionSource<byte[]>) s).TrySetCanceled(cancellationT`
|  | `519` |  | `#else`
| 0 | `520` |  | `using (cancellationToken.Register(s => ((TaskCompletionSource<byte[]>) s).TrySetCanceled(cancellationToken),`
|  | `521` |  | `#endif // NET || NETSTANDARD2_1_OR_GREATER`
| 1 | `522` |  | `{`
| 1 | `523` |  | `SendRequest(new SftpOpenRequest(ProtocolVersion,`
| 1 | `524` |  | `NextRequestId,`
| 1 | `525` |  | `path,`
| 1 | `526` |  | `_encoding,`
| 1 | `527` |  | `flags,`
| 1 | `528` |  | `response => tcs.TrySetResult(response.Handle),`
| 1 | `529` |  | `response => tcs.TrySetException(GetSftpException(response))));`
|  | `530` |  | ``
| 1 | `531` |  | `return await tcs.Task.ConfigureAwait(false);`
|  | `532` |  | `}`
| 1 | `533` |  | `}`
|  | `534` |  | ``
|  | `535` |  | `/// <summary>`
|  | `536` |  | `/// Performs SSH_FXP_OPEN request.`
|  | `537` |  | `/// </summary>`
|  | `538` |  | `/// <param name="path">The path.</param>`
|  | `539` |  | `/// <param name="flags">The flags.</param>`
|  | `540` |  | `/// <param name="callback">The <see cref="AsyncCallback"/> delegate that is executed when <see cref="BeginOpen(s`
|  | `541` |  | `/// <param name="state">An object that contains any additional user-defined data.</param>`
|  | `542` |  | `/// <returns>`
|  | `543` |  | `/// A <see cref="SftpOpenAsyncResult"/> that represents the asynchronous call.`
|  | `544` |  | `/// </returns>`
|  | `545` |  | `public SftpOpenAsyncResult BeginOpen(string path, Flags flags, AsyncCallback callback, object state)`
| 62 | `546` |  | `{`
| 62 | `547` |  | `var asyncResult = new SftpOpenAsyncResult(callback, state);`
|  | `548` |  | ``
| 62 | `549` |  | `var request = new SftpOpenRequest(ProtocolVersion,`
| 62 | `550` |  | `NextRequestId,`
| 62 | `551` |  | `path,`
| 62 | `552` |  | `_encoding,`
| 62 | `553` |  | `flags,`
| 62 | `554` |  | `response =>`
| 58 | `555` |  | `{`
| 58 | `556` |  | `asyncResult.SetAsCompleted(response.Handle, completedSynchronously: fa`
| 58 | `557` |  | `},`
| 62 | `558` |  | `response =>`
| 4 | `559` |  | `{`
| 4 | `560` |  | `asyncResult.SetAsCompleted(GetSftpException(response), completedSynchr`
| 66 | `561` |  | `});`
|  | `562` |  | ``
| 62 | `563` |  | `SendRequest(request);`
|  | `564` |  | ``
| 62 | `565` |  | `return asyncResult;`
| 62 | `566` |  | `}`
|  | `567` |  | ``
|  | `568` |  | `/// <summary>`
|  | `569` |  | `/// Handles the end of an asynchronous open.`
|  | `570` |  | `/// </summary>`
|  | `571` |  | `/// <param name="asyncResult">An <see cref="SftpOpenAsyncResult"/> that represents an asynchronous call.</param>`
|  | `572` |  | `/// <returns>`
|  | `573` |  | `/// A <see cref="byte"/> array representing a file handle.`
|  | `574` |  | `/// </returns>`
|  | `575` |  | `/// <remarks>`
|  | `576` |  | `/// If all available data has been read, the <see cref="EndOpen(SftpOpenAsyncResult)"/> method completes`
|  | `577` |  | `/// immediately and returns zero bytes.`
|  | `578` |  | `/// </remarks>`
|  | `579` |  | `/// <exception cref="ArgumentNullException"><paramref name="asyncResult"/> is <see langword="null"/>.</exception`
|  | `580` |  | `public byte[] EndOpen(SftpOpenAsyncResult asyncResult)`
| 62 | `581` |  | `{`
| 62 | `582` |  | `if (asyncResult is null)`
| 0 | `583` |  | `{`
| 0 | `584` |  | `throw new ArgumentNullException(nameof(asyncResult));`
|  | `585` |  | `}`
|  | `586` |  | ``
| 62 | `587` |  | `if (asyncResult.EndInvokeCalled)`
| 0 | `588` |  | `{`
| 0 | `589` |  | `throw new InvalidOperationException("EndOpen has already been called.");`
|  | `590` |  | `}`
|  | `591` |  | ``
| 62 | `592` |  | `if (asyncResult.IsCompleted)`
| 12 | `593` |  | `{`
| 12 | `594` |  | `return asyncResult.EndInvoke();`
|  | `595` |  | `}`
|  | `596` |  | ``
| 50 | `597` |  | `using (var waitHandle = asyncResult.AsyncWaitHandle)`
| 50 | `598` |  | `{`
| 50 | `599` |  | `WaitOnHandle(waitHandle, OperationTimeout);`
| 50 | `600` |  | `return asyncResult.EndInvoke();`
|  | `601` |  | `}`
| 58 | `602` |  | `}`
|  | `603` |  | ``
|  | `604` |  | `/// <summary>`
|  | `605` |  | `/// Performs SSH_FXP_CLOSE request.`
|  | `606` |  | `/// </summary>`
|  | `607` |  | `/// <param name="handle">The handle.</param>`
|  | `608` |  | `public void RequestClose(byte[] handle)`
| 342 | `609` |  | `{`
| 342 | `610` |  | `SshException exception = null;`
|  | `611` |  | ``
| 342 | `612` |  | `using (var wait = new AutoResetEvent(initialState: false))`
| 342 | `613` |  | `{`
| 342 | `614` |  | `var request = new SftpCloseRequest(ProtocolVersion,`
| 342 | `615` |  | `NextRequestId,`
| 342 | `616` |  | `handle,`
| 342 | `617` |  | `response =>`
| 342 | `618` |  | `{`
| 342 | `619` |  | `exception = GetSftpException(response);`
| 342 | `620` |  | `_ = wait.Set();`
| 684 | `621` |  | `});`
|  | `622` |  | ``
| 342 | `623` |  | `SendRequest(request);`
|  | `624` |  | ``
| 342 | `625` |  | `WaitOnHandle(wait, OperationTimeout);`
| 342 | `626` |  | `}`
|  | `627` |  | ``
|  | `628` |  | `#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 342 | `629` |  | `if (exception is not null)`
|  | `630` |  | `#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 0 | `631` |  | `{`
| 0 | `632` |  | `throw exception;`
|  | `633` |  | `}`
| 342 | `634` |  | `}`
|  | `635` |  | ``
|  | `636` |  | `/// <summary>`
|  | `637` |  | `/// Performs a <c>SSH_FXP_CLOSE</c> request.`
|  | `638` |  | `/// </summary>`
|  | `639` |  | `/// <param name="handle">The handle.</param>`
|  | `640` |  | `/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>`
|  | `641` |  | `/// <returns>`
|  | `642` |  | `/// A task that represents the asynchronous <c>SSH_FXP_CLOSE</c> request.`
|  | `643` |  | `/// </returns>`
|  | `644` |  | `public async Task RequestCloseAsync(byte[] handle, CancellationToken cancellationToken)`
| 2 | `645` |  | `{`
| 2 | `646` |  | `var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);`
|  | `647` |  | ``
| 2 | `648` |  | `SendRequest(new SftpCloseRequest(ProtocolVersion,`
| 2 | `649` |  | `NextRequestId,`
| 2 | `650` |  | `handle,`
| 2 | `651` |  | `response =>`
| 2 | `652` |  | `{`
| 2 | `653` |  | `if (response.StatusCode == StatusCodes.Ok)`
| 2 | `654` |  | `{`
| 2 | `655` |  | `_ = tcs.TrySetResult(true);`
| 2 | `656` |  | `}`
| 2 | `657` |  | `else`
| 0 | `658` |  | `{`
| 0 | `659` |  | `_ = tcs.TrySetException(GetSftpException(response));`
| 0 | `660` |  | `}`
| 4 | `661` |  | `}));`
|  | `662` |  | ``
|  | `663` |  | `// Only check for cancellation after the SftpCloseRequest was sent`
| 2 | `664` |  | `cancellationToken.ThrowIfCancellationRequested();`
|  | `665` |  | ``
|  | `666` |  | `#if NET || NETSTANDARD2_1_OR_GREATER`
| 2 | `667` |  | `await using (cancellationToken.Register(s => ((TaskCompletionSource<bool>) s).TrySetCanceled(cancellationTok`
|  | `668` |  | `#else`
| 0 | `669` |  | `using (cancellationToken.Register(s => ((TaskCompletionSource<bool>) s).TrySetCanceled(cancellationToken), t`
|  | `670` |  | `#endif // NET || NETSTANDARD2_1_OR_GREATER`
| 2 | `671` |  | `{`
| 2 | `672` |  | `_ = await tcs.Task.ConfigureAwait(false);`
| 2 | `673` |  | `}`
| 2 | `674` |  | `}`
|  | `675` |  | ``
|  | `676` |  | `/// <summary>`
|  | `677` |  | `/// Performs SSH_FXP_CLOSE request.`
|  | `678` |  | `/// </summary>`
|  | `679` |  | `/// <param name="handle">The handle.</param>`
|  | `680` |  | `/// <param name="callback">The <see cref="AsyncCallback"/> delegate that is executed when <see cref="BeginClose(`
|  | `681` |  | `/// <param name="state">An object that contains any additional user-defined data.</param>`
|  | `682` |  | `/// <returns>`
|  | `683` |  | `/// A <see cref="SftpCloseAsyncResult"/> that represents the asynchronous call.`
|  | `684` |  | `/// </returns>`
|  | `685` |  | `public SftpCloseAsyncResult BeginClose(byte[] handle, AsyncCallback callback, object state)`
| 46 | `686` |  | `{`
| 46 | `687` |  | `var asyncResult = new SftpCloseAsyncResult(callback, state);`
|  | `688` |  | ``
| 46 | `689` |  | `var request = new SftpCloseRequest(ProtocolVersion,`
| 46 | `690` |  | `NextRequestId,`
| 46 | `691` |  | `handle,`
| 46 | `692` |  | `response =>`
| 46 | `693` |  | `{`
| 46 | `694` |  | `asyncResult.SetAsCompleted(GetSftpException(response), completedSynch`
| 92 | `695` |  | `});`
| 46 | `696` |  | `SendRequest(request);`
|  | `697` |  | ``
| 46 | `698` |  | `return asyncResult;`
| 46 | `699` |  | `}`
|  | `700` |  | ``
|  | `701` |  | `/// <summary>`
|  | `702` |  | `/// Handles the end of an asynchronous close.`
|  | `703` |  | `/// </summary>`
|  | `704` |  | `/// <param name="asyncResult">An <see cref="SftpCloseAsyncResult"/> that represents an asynchronous call.</param`
|  | `705` |  | `/// <exception cref="ArgumentNullException"><paramref name="asyncResult"/> is <see langword="null"/>.</exception`
|  | `706` |  | `public void EndClose(SftpCloseAsyncResult asyncResult)`
| 46 | `707` |  | `{`
| 46 | `708` |  | `if (asyncResult is null)`
| 0 | `709` |  | `{`
| 0 | `710` |  | `throw new ArgumentNullException(nameof(asyncResult));`
|  | `711` |  | `}`
|  | `712` |  | ``
| 46 | `713` |  | `if (asyncResult.EndInvokeCalled)`
| 0 | `714` |  | `{`
| 0 | `715` |  | `throw new InvalidOperationException("EndClose has already been called.");`
|  | `716` |  | `}`
|  | `717` |  | ``
| 46 | `718` |  | `if (asyncResult.IsCompleted)`
| 0 | `719` |  | `{`
| 0 | `720` |  | `asyncResult.EndInvoke();`
| 0 | `721` |  | `}`
|  | `722` |  | `else`
| 46 | `723` |  | `{`
| 46 | `724` |  | `using (var waitHandle = asyncResult.AsyncWaitHandle)`
| 46 | `725` |  | `{`
| 46 | `726` |  | `WaitOnHandle(waitHandle, OperationTimeout);`
| 46 | `727` |  | `asyncResult.EndInvoke();`
| 46 | `728` |  | `}`
| 46 | `729` |  | `}`
| 46 | `730` |  | `}`
|  | `731` |  | ``
|  | `732` |  | `/// <summary>`
|  | `733` |  | `/// Begins an asynchronous read using a SSH_FXP_READ request.`
|  | `734` |  | `/// </summary>`
|  | `735` |  | `/// <param name="handle">The handle to the file to read from.</param>`
|  | `736` |  | `/// <param name="offset">The offset in the file to start reading from.</param>`
|  | `737` |  | `/// <param name="length">The number of bytes to read.</param>`
|  | `738` |  | `/// <param name="callback">The <see cref="AsyncCallback"/> delegate that is executed when <see cref="BeginRead(b`
|  | `739` |  | `/// <param name="state">An object that contains any additional user-defined data.</param>`
|  | `740` |  | `/// <returns>`
|  | `741` |  | `/// A <see cref="SftpReadAsyncResult"/> that represents the asynchronous call.`
|  | `742` |  | `/// </returns>`
|  | `743` |  | `public SftpReadAsyncResult BeginRead(byte[] handle, ulong offset, uint length, AsyncCallback callback, object st`
| 3705 | `744` |  | `{`
| 3705 | `745` |  | `var asyncResult = new SftpReadAsyncResult(callback, state);`
|  | `746` |  | ``
| 3705 | `747` |  | `var request = new SftpReadRequest(ProtocolVersion,`
| 3705 | `748` |  | `NextRequestId,`
| 3705 | `749` |  | `handle,`
| 3705 | `750` |  | `offset,`
| 3705 | `751` |  | `length,`
| 3705 | `752` |  | `response =>`
| 3659 | `753` |  | `{`
| 3659 | `754` |  | `asyncResult.SetAsCompleted(response.Data, completedSynchronously: fals`
| 3659 | `755` |  | `},`
| 3705 | `756` |  | `response =>`
| 46 | `757` |  | `{`
| 46 | `758` |  | `if (response.StatusCode != StatusCodes.Eof)`
| 0 | `759` |  | `{`
| 0 | `760` |  | `asyncResult.SetAsCompleted(GetSftpException(response), completedSy`
| 0 | `761` |  | `}`
| 3705 | `762` |  | `else`
| 46 | `763` |  | `{`
| 46 | `764` |  | `asyncResult.SetAsCompleted(Array.Empty<byte>(), completedSynchrono`
| 46 | `765` |  | `}`
| 3751 | `766` |  | `});`
| 3705 | `767` |  | `SendRequest(request);`
|  | `768` |  | ``
| 3705 | `769` |  | `return asyncResult;`
| 3705 | `770` |  | `}`
|  | `771` |  | ``
|  | `772` |  | `/// <summary>`
|  | `773` |  | `/// Handles the end of an asynchronous read.`
|  | `774` |  | `/// </summary>`
|  | `775` |  | `/// <param name="asyncResult">An <see cref="SftpReadAsyncResult"/> that represents an asynchronous call.</param>`
|  | `776` |  | `/// <returns>`
|  | `777` |  | `/// A <see cref="byte"/> array representing the data read.`
|  | `778` |  | `/// </returns>`
|  | `779` |  | `/// <remarks>`
|  | `780` |  | `/// If all available data has been read, the <see cref="EndRead(SftpReadAsyncResult)"/> method completes`
|  | `781` |  | `/// immediately and returns zero bytes.`
|  | `782` |  | `/// </remarks>`
|  | `783` |  | `/// <exception cref="ArgumentNullException"><paramref name="asyncResult"/> is <see langword="null"/>.</exception`
|  | `784` |  | `public byte[] EndRead(SftpReadAsyncResult asyncResult)`
| 56 | `785` |  | `{`
| 56 | `786` |  | `if (asyncResult is null)`
| 0 | `787` |  | `{`
| 0 | `788` |  | `throw new ArgumentNullException(nameof(asyncResult));`
|  | `789` |  | `}`
|  | `790` |  | ``
| 56 | `791` |  | `if (asyncResult.EndInvokeCalled)`
| 0 | `792` |  | `{`
| 0 | `793` |  | `throw new InvalidOperationException("EndRead has already been called.");`
|  | `794` |  | `}`
|  | `795` |  | ``
| 56 | `796` |  | `if (asyncResult.IsCompleted)`
| 12 | `797` |  | `{`
| 12 | `798` |  | `return asyncResult.EndInvoke();`
|  | `799` |  | `}`
|  | `800` |  | ``
| 44 | `801` |  | `using (var waitHandle = asyncResult.AsyncWaitHandle)`
| 44 | `802` |  | `{`
| 44 | `803` |  | `WaitOnHandle(waitHandle, OperationTimeout);`
| 44 | `804` |  | `return asyncResult.EndInvoke();`
|  | `805` |  | `}`
| 56 | `806` |  | `}`
|  | `807` |  | ``
|  | `808` |  | `/// <summary>`
|  | `809` |  | `/// Performs SSH_FXP_READ request.`
|  | `810` |  | `/// </summary>`
|  | `811` |  | `/// <param name="handle">The handle.</param>`
|  | `812` |  | `/// <param name="offset">The offset.</param>`
|  | `813` |  | `/// <param name="length">The length.</param>`
|  | `814` |  | `/// <returns>`
|  | `815` |  | `/// The data that was read, or an empty array when the end of the file was reached.`
|  | `816` |  | `/// </returns>`
|  | `817` |  | `public byte[] RequestRead(byte[] handle, ulong offset, uint length)`
| 375 | `818` |  | `{`
| 375 | `819` |  | `SshException exception = null;`
|  | `820` |  | ``
| 375 | `821` |  | `byte[] data = null;`
|  | `822` |  | ``
| 375 | `823` |  | `using (var wait = new AutoResetEvent(initialState: false))`
| 375 | `824` |  | `{`
| 375 | `825` |  | `var request = new SftpReadRequest(ProtocolVersion,`
| 375 | `826` |  | `NextRequestId,`
| 375 | `827` |  | `handle,`
| 375 | `828` |  | `offset,`
| 375 | `829` |  | `length,`
| 375 | `830` |  | `response =>`
| 308 | `831` |  | `{`
| 308 | `832` |  | `data = response.Data;`
| 308 | `833` |  | `_ = wait.Set();`
| 308 | `834` |  | `},`
| 375 | `835` |  | `response =>`
| 67 | `836` |  | `{`
| 67 | `837` |  | `if (response.StatusCode != StatusCodes.Eof)`
| 0 | `838` |  | `{`
| 0 | `839` |  | `exception = GetSftpException(response);`
| 0 | `840` |  | `}`
| 375 | `841` |  | `else`
| 67 | `842` |  | `{`
| 67 | `843` |  | `data = Array.Empty<byte>();`
| 67 | `844` |  | `}`
| 375 | `845` |  | ``
| 67 | `846` |  | `_ = wait.Set();`
| 442 | `847` |  | `});`
|  | `848` |  | ``
| 375 | `849` |  | `SendRequest(request);`
|  | `850` |  | ``
| 375 | `851` |  | `WaitOnHandle(wait, OperationTimeout);`
| 375 | `852` |  | `}`
|  | `853` |  | ``
|  | `854` |  | `#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 375 | `855` |  | `if (exception is not null)`
|  | `856` |  | `#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 0 | `857` |  | `{`
| 0 | `858` |  | `throw exception;`
|  | `859` |  | `}`
|  | `860` |  | ``
| 375 | `861` |  | `return data;`
| 375 | `862` |  | `}`
|  | `863` |  | ``
|  | `864` |  | `/// <summary>`
|  | `865` |  | `/// Asynchronously performs a <c>SSH_FXP_READ</c> request.`
|  | `866` |  | `/// </summary>`
|  | `867` |  | `/// <param name="handle">The handle to the file to read from.</param>`
|  | `868` |  | `/// <param name="offset">The offset in the file to start reading from.</param>`
|  | `869` |  | `/// <param name="length">The number of bytes to read.</param>`
|  | `870` |  | `/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>`
|  | `871` |  | `/// <returns>`
|  | `872` |  | `/// A task that represents the asynchronous <c>SSH_FXP_READ</c> request. The value of`
|  | `873` |  | `/// its <see cref="Task{Task}.Result"/> contains the data read from the file, or an empty`
|  | `874` |  | `/// array when the end of the file is reached.`
|  | `875` |  | `/// </returns>`
|  | `876` |  | `public async Task<byte[]> RequestReadAsync(byte[] handle, ulong offset, uint length, CancellationToken cancellat`
| 0 | `877` |  | `{`
| 0 | `878` |  | `cancellationToken.ThrowIfCancellationRequested();`
|  | `879` |  | ``
| 0 | `880` |  | `var tcs = new TaskCompletionSource<byte[]>(TaskCreationOptions.RunContinuationsAsynchronously);`
|  | `881` |  | ``
|  | `882` |  | `#if NET || NETSTANDARD2_1_OR_GREATER`
| 0 | `883` |  | `await using (cancellationToken.Register(s => ((TaskCompletionSource<byte[]>) s).TrySetCanceled(cancellationT`
|  | `884` |  | `#else`
| 0 | `885` |  | `using (cancellationToken.Register(s => ((TaskCompletionSource<byte[]>) s).TrySetCanceled(cancellationToken),`
|  | `886` |  | `#endif // NET || NETSTANDARD2_1_OR_GREATER`
| 0 | `887` |  | `{`
| 0 | `888` |  | `SendRequest(new SftpReadRequest(ProtocolVersion,`
| 0 | `889` |  | `NextRequestId,`
| 0 | `890` |  | `handle,`
| 0 | `891` |  | `offset,`
| 0 | `892` |  | `length,`
| 0 | `893` |  | `response => tcs.TrySetResult(response.Data),`
| 0 | `894` |  | `response =>`
| 0 | `895` |  | `{`
| 0 | `896` |  | `if (response.StatusCode == StatusCodes.Eof)`
| 0 | `897` |  | `{`
| 0 | `898` |  | `_ = tcs.TrySetResult(Array.Empty<byte>());`
| 0 | `899` |  | `}`
| 0 | `900` |  | `else`
| 0 | `901` |  | `{`
| 0 | `902` |  | `_ = tcs.TrySetException(GetSftpException(response));`
| 0 | `903` |  | `}`
| 0 | `904` |  | `}));`
|  | `905` |  | ``
| 0 | `906` |  | `return await tcs.Task.ConfigureAwait(false);`
|  | `907` |  | `}`
| 0 | `908` |  | `}`
|  | `909` |  | ``
|  | `910` |  | `/// <summary>`
|  | `911` |  | `/// Performs SSH_FXP_WRITE request.`
|  | `912` |  | `/// </summary>`
|  | `913` |  | `/// <param name="handle">The handle.</param>`
|  | `914` |  | `/// <param name="serverOffset">The the zero-based offset (in bytes) relative to the beginning of the file that t`
|  | `915` |  | `/// <param name="data">The buffer holding the data to write.</param>`
|  | `916` |  | `/// <param name="offset">the zero-based offset in <paramref name="data" /> at which to begin taking bytes to wri`
|  | `917` |  | `/// <param name="length">The length (in bytes) of the data to write.</param>`
|  | `918` |  | `/// <param name="wait">The wait event handle if needed.</param>`
|  | `919` |  | `/// <param name="writeCompleted">The callback to invoke when the write has completed.</param>`
|  | `920` |  | `public void RequestWrite(byte[] handle,`
|  | `921` |  | `ulong serverOffset,`
|  | `922` |  | `byte[] data,`
|  | `923` |  | `int offset,`
|  | `924` |  | `int length,`
|  | `925` |  | `AutoResetEvent wait,`
|  | `926` |  | `Action<SftpStatusResponse> writeCompleted = null)`
| 3898 | `927` |  | `{`
| 3898 | `928` |  | `SshException exception = null;`
|  | `929` |  | ``
| 3898 | `930` |  | `var request = new SftpWriteRequest(ProtocolVersion,`
| 3898 | `931` |  | `NextRequestId,`
| 3898 | `932` |  | `handle,`
| 3898 | `933` |  | `serverOffset,`
| 3898 | `934` |  | `data,`
| 3898 | `935` |  | `offset,`
| 3898 | `936` |  | `length,`
| 3898 | `937` |  | `response =>`
| 3898 | `938` |  | `{`
| 3898 | `939` |  | `writeCompleted?.Invoke(response);`
| 3898 | `940` |  | ``
| 3898 | `941` |  | `exception = GetSftpException(response);`
| 3898 | `942` |  | `if (wait != null)`
| 157 | `943` |  | `{`
| 157 | `944` |  | `_ = wait.Set();`
| 157 | `945` |  | `}`
| 7796 | `946` |  | `});`
|  | `947` |  | ``
| 3898 | `948` |  | `SendRequest(request);`
|  | `949` |  | ``
| 3898 | `950` |  | `if (wait is not null)`
| 157 | `951` |  | `{`
| 157 | `952` |  | `WaitOnHandle(wait, OperationTimeout);`
| 157 | `953` |  | `}`
|  | `954` |  | ``
|  | `955` |  | `#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 3898 | `956` |  | `if (exception is not null)`
|  | `957` |  | `#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 0 | `958` |  | `{`
| 0 | `959` |  | `throw exception;`
|  | `960` |  | `}`
| 3898 | `961` |  | `}`
|  | `962` |  | ``
|  | `963` |  | `/// <summary>`
|  | `964` |  | `/// Asynchronouly performs a <c>SSH_FXP_WRITE</c> request.`
|  | `965` |  | `/// </summary>`
|  | `966` |  | `/// <param name="handle">The handle.</param>`
|  | `967` |  | `/// <param name="serverOffset">The the zero-based offset (in bytes) relative to the beginning of the file that t`
|  | `968` |  | `/// <param name="data">The buffer holding the data to write.</param>`
|  | `969` |  | `/// <param name="offset">the zero-based offset in <paramref name="data" /> at which to begin taking bytes to wri`
|  | `970` |  | `/// <param name="length">The length (in bytes) of the data to write.</param>`
|  | `971` |  | `/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>`
|  | `972` |  | `/// <returns>`
|  | `973` |  | `/// A task that represents the asynchronous <c>SSH_FXP_WRITE</c> request.`
|  | `974` |  | `/// </returns>`
|  | `975` |  | `public async Task RequestWriteAsync(byte[] handle, ulong serverOffset, byte[] data, int offset, int length, Canc`
| 32 | `976` |  | `{`
| 32 | `977` |  | `cancellationToken.ThrowIfCancellationRequested();`
|  | `978` |  | ``
| 32 | `979` |  | `var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);`
|  | `980` |  | ``
|  | `981` |  | `#if NET || NETSTANDARD2_1_OR_GREATER`
| 32 | `982` |  | `await using (cancellationToken.Register(s => ((TaskCompletionSource<bool>) s).TrySetCanceled(cancellationTok`
|  | `983` |  | `#else`
| 0 | `984` |  | `using (cancellationToken.Register(s => ((TaskCompletionSource<bool>) s).TrySetCanceled(cancellationToken), t`
|  | `985` |  | `#endif // NET || NETSTANDARD2_1_OR_GREATER`
| 32 | `986` |  | `{`
| 32 | `987` |  | `SendRequest(new SftpWriteRequest(ProtocolVersion,`
| 32 | `988` |  | `NextRequestId,`
| 32 | `989` |  | `handle,`
| 32 | `990` |  | `serverOffset,`
| 32 | `991` |  | `data,`
| 32 | `992` |  | `offset,`
| 32 | `993` |  | `length,`
| 32 | `994` |  | `response =>`
| 32 | `995` |  | `{`
| 32 | `996` |  | `if (response.StatusCode == StatusCodes.Ok)`
| 32 | `997` |  | `{`
| 32 | `998` |  | `_ = tcs.TrySetResult(true);`
| 32 | `999` |  | `}`
| 32 | `1000` |  | `else`
| 0 | `1001` |  | `{`
| 0 | `1002` |  | `_ = tcs.TrySetException(GetSftpException(response));`
| 0 | `1003` |  | `}`
| 64 | `1004` |  | `}));`
|  | `1005` |  | ``
| 32 | `1006` |  | `_ = await tcs.Task.ConfigureAwait(false);`
| 32 | `1007` |  | `}`
| 32 | `1008` |  | `}`
|  | `1009` |  | ``
|  | `1010` |  | `/// <summary>`
|  | `1011` |  | `/// Performs SSH_FXP_LSTAT request.`
|  | `1012` |  | `/// </summary>`
|  | `1013` |  | `/// <param name="path">The path.</param>`
|  | `1014` |  | `/// <returns>`
|  | `1015` |  | `/// File attributes.`
|  | `1016` |  | `/// </returns>`
|  | `1017` |  | `public SftpFileAttributes RequestLStat(string path)`
| 505 | `1018` |  | `{`
| 505 | `1019` |  | `SshException exception = null;`
|  | `1020` |  | ``
| 505 | `1021` |  | `SftpFileAttributes attributes = null;`
| 505 | `1022` |  | `using (var wait = new AutoResetEvent(initialState: false))`
| 505 | `1023` |  | `{`
| 505 | `1024` |  | `var request = new SftpLStatRequest(ProtocolVersion,`
| 505 | `1025` |  | `NextRequestId,`
| 505 | `1026` |  | `path,`
| 505 | `1027` |  | `_encoding,`
| 505 | `1028` |  | `response =>`
| 250 | `1029` |  | `{`
| 250 | `1030` |  | `attributes = response.Attributes;`
| 250 | `1031` |  | `_ = wait.Set();`
| 250 | `1032` |  | `},`
| 505 | `1033` |  | `response =>`
| 255 | `1034` |  | `{`
| 255 | `1035` |  | `exception = GetSftpException(response);`
| 255 | `1036` |  | `_ = wait.Set();`
| 760 | `1037` |  | `});`
|  | `1038` |  | ``
| 505 | `1039` |  | `SendRequest(request);`
|  | `1040` |  | ``
| 505 | `1041` |  | `WaitOnHandle(wait, OperationTimeout);`
| 505 | `1042` |  | `}`
|  | `1043` |  | ``
|  | `1044` |  | `#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 505 | `1045` |  | `if (exception is not null)`
|  | `1046` |  | `#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 255 | `1047` |  | `{`
| 255 | `1048` |  | `throw exception;`
|  | `1049` |  | `}`
|  | `1050` |  | ``
| 250 | `1051` |  | `return attributes;`
| 250 | `1052` |  | `}`
|  | `1053` |  | ``
|  | `1054` |  | `/// <summary>`
|  | `1055` |  | `/// Performs SSH_FXP_LSTAT request.`
|  | `1056` |  | `/// </summary>`
|  | `1057` |  | `/// <param name="path">The path.</param>`
|  | `1058` |  | `/// <param name="callback">The <see cref="AsyncCallback"/> delegate that is executed when <see cref="BeginLStat(`
|  | `1059` |  | `/// <param name="state">An object that contains any additional user-defined data.</param>`
|  | `1060` |  | `/// <returns>`
|  | `1061` |  | `/// A <see cref="SFtpStatAsyncResult"/> that represents the asynchronous call.`
|  | `1062` |  | `/// </returns>`
|  | `1063` |  | `public SFtpStatAsyncResult BeginLStat(string path, AsyncCallback callback, object state)`
| 46 | `1064` |  | `{`
| 46 | `1065` |  | `var asyncResult = new SFtpStatAsyncResult(callback, state);`
|  | `1066` |  | ``
| 46 | `1067` |  | `var request = new SftpLStatRequest(ProtocolVersion,`
| 46 | `1068` |  | `NextRequestId,`
| 46 | `1069` |  | `path,`
| 46 | `1070` |  | `_encoding,`
| 46 | `1071` |  | `response =>`
| 46 | `1072` |  | `{`
| 46 | `1073` |  | `asyncResult.SetAsCompleted(response.Attributes, completedSynchronousl`
| 46 | `1074` |  | `},`
| 46 | `1075` |  | `response =>`
| 0 | `1076` |  | `{`
| 0 | `1077` |  | `asyncResult.SetAsCompleted(GetSftpException(response), completedSynch`
| 46 | `1078` |  | `});`
| 46 | `1079` |  | `SendRequest(request);`
|  | `1080` |  | ``
| 46 | `1081` |  | `return asyncResult;`
| 46 | `1082` |  | `}`
|  | `1083` |  | ``
|  | `1084` |  | `/// <summary>`
|  | `1085` |  | `/// Handles the end of an asynchronous SSH_FXP_LSTAT request.`
|  | `1086` |  | `/// </summary>`
|  | `1087` |  | `/// <param name="asyncResult">An <see cref="SFtpStatAsyncResult"/> that represents an asynchronous call.</param>`
|  | `1088` |  | `/// <returns>`
|  | `1089` |  | `/// The file attributes.`
|  | `1090` |  | `/// </returns>`
|  | `1091` |  | `/// <exception cref="ArgumentNullException"><paramref name="asyncResult"/> is <see langword="null"/>.</exception`
|  | `1092` |  | `public SftpFileAttributes EndLStat(SFtpStatAsyncResult asyncResult)`
| 46 | `1093` |  | `{`
| 46 | `1094` |  | `if (asyncResult is null)`
| 0 | `1095` |  | `{`
| 0 | `1096` |  | `throw new ArgumentNullException(nameof(asyncResult));`
|  | `1097` |  | `}`
|  | `1098` |  | ``
| 46 | `1099` |  | `if (asyncResult.EndInvokeCalled)`
| 0 | `1100` |  | `{`
| 0 | `1101` |  | `throw new InvalidOperationException("EndLStat has already been called.");`
|  | `1102` |  | `}`
|  | `1103` |  | ``
| 46 | `1104` |  | `if (asyncResult.IsCompleted)`
| 1 | `1105` |  | `{`
| 1 | `1106` |  | `return asyncResult.EndInvoke();`
|  | `1107` |  | `}`
|  | `1108` |  | ``
| 45 | `1109` |  | `using (var waitHandle = asyncResult.AsyncWaitHandle)`
| 45 | `1110` |  | `{`
| 45 | `1111` |  | `WaitOnHandle(waitHandle, OperationTimeout);`
| 45 | `1112` |  | `return asyncResult.EndInvoke();`
|  | `1113` |  | `}`
| 46 | `1114` |  | `}`
|  | `1115` |  | ``
|  | `1116` |  | `/// <summary>`
|  | `1117` |  | `/// Performs SSH_FXP_FSTAT request.`
|  | `1118` |  | `/// </summary>`
|  | `1119` |  | `/// <param name="handle">The handle.</param>`
|  | `1120` |  | `/// <param name="nullOnError">If set to <see langword="true"/>, returns <see langword="null"/> instead of throwi`
|  | `1121` |  | `/// <returns>`
|  | `1122` |  | `/// File attributes.`
|  | `1123` |  | `/// </returns>`
|  | `1124` |  | `public SftpFileAttributes RequestFStat(byte[] handle, bool nullOnError)`
| 113 | `1125` |  | `{`
| 113 | `1126` |  | `SshException exception = null;`
| 113 | `1127` |  | `SftpFileAttributes attributes = null;`
|  | `1128` |  | ``
| 113 | `1129` |  | `using (var wait = new AutoResetEvent(initialState: false))`
| 113 | `1130` |  | `{`
| 113 | `1131` |  | `var request = new SftpFStatRequest(ProtocolVersion,`
| 113 | `1132` |  | `NextRequestId,`
| 113 | `1133` |  | `handle,`
| 113 | `1134` |  | `response =>`
| 113 | `1135` |  | `{`
| 113 | `1136` |  | `attributes = response.Attributes;`
| 113 | `1137` |  | `_ = wait.Set();`
| 113 | `1138` |  | `},`
| 113 | `1139` |  | `response =>`
| 0 | `1140` |  | `{`
| 0 | `1141` |  | `exception = GetSftpException(response);`
| 0 | `1142` |  | `_ = wait.Set();`
| 113 | `1143` |  | `});`
|  | `1144` |  | ``
| 113 | `1145` |  | `SendRequest(request);`
|  | `1146` |  | ``
| 113 | `1147` |  | `WaitOnHandle(wait, OperationTimeout);`
| 113 | `1148` |  | `}`
|  | `1149` |  | ``
|  | `1150` |  | `#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 113 | `1151` |  | `if (!nullOnError && exception is not null)`
|  | `1152` |  | `#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 0 | `1153` |  | `{`
| 0 | `1154` |  | `throw exception;`
|  | `1155` |  | `}`
|  | `1156` |  | ``
| 113 | `1157` |  | `return attributes;`
| 113 | `1158` |  | `}`
|  | `1159` |  | ``
|  | `1160` |  | `/// <summary>`
|  | `1161` |  | `/// Asynchronously performs a <c>SSH_FXP_FSTAT</c> request.`
|  | `1162` |  | `/// </summary>`
|  | `1163` |  | `/// <param name="handle">The handle.</param>`
|  | `1164` |  | `/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>`
|  | `1165` |  | `/// <returns>`
|  | `1166` |  | `/// A task that represents the asynchronous <c>SSH_FXP_FSTAT</c> request. The value of its`
|  | `1167` |  | `/// <see cref="Task{Task}.Result"/> contains the file attributes of the specified handle.`
|  | `1168` |  | `/// </returns>`
|  | `1169` |  | `public async Task<SftpFileAttributes> RequestFStatAsync(byte[] handle, CancellationToken cancellationToken)`
| 0 | `1170` |  | `{`
| 0 | `1171` |  | `cancellationToken.ThrowIfCancellationRequested();`
|  | `1172` |  | ``
| 0 | `1173` |  | `var tcs = new TaskCompletionSource<SftpFileAttributes>(TaskCreationOptions.RunContinuationsAsynchronously);`
|  | `1174` |  | ``
|  | `1175` |  | `#if NET || NETSTANDARD2_1_OR_GREATER`
| 0 | `1176` |  | `await using (cancellationToken.Register(s => ((TaskCompletionSource<SftpFileAttributes>) s).TrySetCanceled(c`
|  | `1177` |  | `#else`
| 0 | `1178` |  | `using (cancellationToken.Register(s => ((TaskCompletionSource<SftpFileAttributes>) s).TrySetCanceled(cancell`
|  | `1179` |  | `#endif // NET || NETSTANDARD2_1_OR_GREATER`
| 0 | `1180` |  | `{`
| 0 | `1181` |  | `SendRequest(new SftpFStatRequest(ProtocolVersion,`
| 0 | `1182` |  | `NextRequestId,`
| 0 | `1183` |  | `handle,`
| 0 | `1184` |  | `response => tcs.TrySetResult(response.Attributes),`
| 0 | `1185` |  | `response => tcs.TrySetException(GetSftpException(response))));`
|  | `1186` |  | ``
| 0 | `1187` |  | `return await tcs.Task.ConfigureAwait(false);`
|  | `1188` |  | `}`
| 0 | `1189` |  | `}`
|  | `1190` |  | ``
|  | `1191` |  | `/// <summary>`
|  | `1192` |  | `/// Performs SSH_FXP_SETSTAT request.`
|  | `1193` |  | `/// </summary>`
|  | `1194` |  | `/// <param name="path">The path.</param>`
|  | `1195` |  | `/// <param name="attributes">The attributes.</param>`
|  | `1196` |  | `public void RequestSetStat(string path, SftpFileAttributes attributes)`
| 5 | `1197` |  | `{`
| 5 | `1198` |  | `SshException exception = null;`
|  | `1199` |  | ``
| 5 | `1200` |  | `using (var wait = new AutoResetEvent(initialState: false))`
| 5 | `1201` |  | `{`
| 5 | `1202` |  | `var request = new SftpSetStatRequest(ProtocolVersion,`
| 5 | `1203` |  | `NextRequestId,`
| 5 | `1204` |  | `path,`
| 5 | `1205` |  | `_encoding,`
| 5 | `1206` |  | `attributes,`
| 5 | `1207` |  | `response =>`
| 5 | `1208` |  | `{`
| 5 | `1209` |  | `exception = GetSftpException(response);`
| 5 | `1210` |  | `_ = wait.Set();`
| 10 | `1211` |  | `});`
|  | `1212` |  | ``
| 5 | `1213` |  | `SendRequest(request);`
|  | `1214` |  | ``
| 5 | `1215` |  | `WaitOnHandle(wait, OperationTimeout);`
| 5 | `1216` |  | `}`
|  | `1217` |  | ``
|  | `1218` |  | `#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 5 | `1219` |  | `if (exception is not null)`
|  | `1220` |  | `#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 0 | `1221` |  | `{`
| 0 | `1222` |  | `throw exception;`
|  | `1223` |  | `}`
| 5 | `1224` |  | `}`
|  | `1225` |  | ``
|  | `1226` |  | `/// <summary>`
|  | `1227` |  | `/// Performs SSH_FXP_FSETSTAT request.`
|  | `1228` |  | `/// </summary>`
|  | `1229` |  | `/// <param name="handle">The handle.</param>`
|  | `1230` |  | `/// <param name="attributes">The attributes.</param>`
|  | `1231` |  | `public void RequestFSetStat(byte[] handle, SftpFileAttributes attributes)`
| 4 | `1232` |  | `{`
| 4 | `1233` |  | `SshException exception = null;`
|  | `1234` |  | ``
| 4 | `1235` |  | `using (var wait = new AutoResetEvent(initialState: false))`
| 4 | `1236` |  | `{`
| 4 | `1237` |  | `var request = new SftpFSetStatRequest(ProtocolVersion,`
| 4 | `1238` |  | `NextRequestId,`
| 4 | `1239` |  | `handle,`
| 4 | `1240` |  | `attributes,`
| 4 | `1241` |  | `response =>`
| 4 | `1242` |  | `{`
| 4 | `1243` |  | `exception = GetSftpException(response);`
| 4 | `1244` |  | `_ = wait.Set();`
| 8 | `1245` |  | `});`
|  | `1246` |  | ``
| 4 | `1247` |  | `SendRequest(request);`
|  | `1248` |  | ``
| 4 | `1249` |  | `WaitOnHandle(wait, OperationTimeout);`
| 4 | `1250` |  | `}`
|  | `1251` |  | ``
|  | `1252` |  | `#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 4 | `1253` |  | `if (exception is not null)`
|  | `1254` |  | `#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 0 | `1255` |  | `{`
| 0 | `1256` |  | `throw exception;`
|  | `1257` |  | `}`
| 4 | `1258` |  | `}`
|  | `1259` |  | ``
|  | `1260` |  | `/// <summary>`
|  | `1261` |  | `/// Performs SSH_FXP_OPENDIR request.`
|  | `1262` |  | `/// </summary>`
|  | `1263` |  | `/// <param name="path">The path.</param>`
|  | `1264` |  | `/// <param name="nullOnError">If set to <see langword="true"/>, returns <see langword="null"/> instead of throwi`
|  | `1265` |  | `/// <returns>File handle.</returns>`
|  | `1266` |  | `public byte[] RequestOpenDir(string path, bool nullOnError = false)`
| 36 | `1267` |  | `{`
| 36 | `1268` |  | `SshException exception = null;`
|  | `1269` |  | ``
| 36 | `1270` |  | `byte[] handle = null;`
|  | `1271` |  | ``
| 36 | `1272` |  | `using (var wait = new AutoResetEvent(initialState: false))`
| 36 | `1273` |  | `{`
| 36 | `1274` |  | `var request = new SftpOpenDirRequest(ProtocolVersion,`
| 36 | `1275` |  | `NextRequestId,`
| 36 | `1276` |  | `path,`
| 36 | `1277` |  | `_encoding,`
| 36 | `1278` |  | `response =>`
| 27 | `1279` |  | `{`
| 27 | `1280` |  | `handle = response.Handle;`
| 27 | `1281` |  | `_ = wait.Set();`
| 27 | `1282` |  | `},`
| 36 | `1283` |  | `response =>`
| 9 | `1284` |  | `{`
| 9 | `1285` |  | `exception = GetSftpException(response);`
| 9 | `1286` |  | `_ = wait.Set();`
| 45 | `1287` |  | `});`
|  | `1288` |  | ``
| 36 | `1289` |  | `SendRequest(request);`
|  | `1290` |  | ``
| 36 | `1291` |  | `WaitOnHandle(wait, OperationTimeout);`
| 36 | `1292` |  | `}`
|  | `1293` |  | ``
|  | `1294` |  | `#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 36 | `1295` |  | `if (!nullOnError && exception is not null)`
|  | `1296` |  | `#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 9 | `1297` |  | `{`
| 9 | `1298` |  | `throw exception;`
|  | `1299` |  | `}`
|  | `1300` |  | ``
| 27 | `1301` |  | `return handle;`
| 27 | `1302` |  | `}`
|  | `1303` |  | ``
|  | `1304` |  | `/// <summary>`
|  | `1305` |  | `/// Asynchronously performs a <c>SSH_FXP_OPENDIR</c> request.`
|  | `1306` |  | `/// </summary>`
|  | `1307` |  | `/// <param name="path">The path.</param>`
|  | `1308` |  | `/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>`
|  | `1309` |  | `/// <returns>`
|  | `1310` |  | `/// A task that represents the asynchronous <c>SSH_FXP_OPENDIR</c> request. The value of its`
|  | `1311` |  | `/// <see cref="Task{Task}.Result"/> contains the handle of the specified path.`
|  | `1312` |  | `/// </returns>`
|  | `1313` |  | `public async Task<byte[]> RequestOpenDirAsync(string path, CancellationToken cancellationToken)`
| 2 | `1314` |  | `{`
| 2 | `1315` |  | `cancellationToken.ThrowIfCancellationRequested();`
|  | `1316` |  | ``
| 2 | `1317` |  | `var tcs = new TaskCompletionSource<byte[]>(TaskCreationOptions.RunContinuationsAsynchronously);`
|  | `1318` |  | ``
|  | `1319` |  | `#if NET || NETSTANDARD2_1_OR_GREATER`
| 2 | `1320` |  | `await using (cancellationToken.Register(s => ((TaskCompletionSource<byte[]>) s).TrySetCanceled(cancellationT`
|  | `1321` |  | `#else`
| 0 | `1322` |  | `using (cancellationToken.Register(s => ((TaskCompletionSource<byte[]>) s).TrySetCanceled(cancellationToken),`
|  | `1323` |  | `#endif // NET || NETSTANDARD2_1_OR_GREATER`
| 2 | `1324` |  | `{`
| 2 | `1325` |  | `SendRequest(new SftpOpenDirRequest(ProtocolVersion,`
| 2 | `1326` |  | `NextRequestId,`
| 2 | `1327` |  | `path,`
| 2 | `1328` |  | `_encoding,`
| 2 | `1329` |  | `response => tcs.TrySetResult(response.Handle),`
| 2 | `1330` |  | `response => tcs.TrySetException(GetSftpException(response))));`
|  | `1331` |  | ``
| 2 | `1332` |  | `return await tcs.Task.ConfigureAwait(false);`
|  | `1333` |  | `}`
| 2 | `1334` |  | `}`
|  | `1335` |  | ``
|  | `1336` |  | `/// <summary>`
|  | `1337` |  | `/// Performs SSH_FXP_READDIR request.`
|  | `1338` |  | `/// </summary>`
|  | `1339` |  | `/// <param name="handle">The handle of the directory to read.</param>`
|  | `1340` |  | `/// <returns>`
|  | `1341` |  | `/// A <see cref="Dictionary{TKey,TValue}"/> where the <c>key</c> is the name of a file in`
|  | `1342` |  | `/// the directory and the <c>value</c> is the <see cref="SftpFileAttributes"/> of the file.`
|  | `1343` |  | `/// </returns>`
|  | `1344` |  | `public KeyValuePair<string, SftpFileAttributes>[] RequestReadDir(byte[] handle)`
| 124 | `1345` |  | `{`
| 124 | `1346` |  | `SshException exception = null;`
|  | `1347` |  | ``
| 124 | `1348` |  | `KeyValuePair<string, SftpFileAttributes>[] result = null;`
|  | `1349` |  | ``
| 124 | `1350` |  | `using (var wait = new AutoResetEvent(initialState: false))`
| 124 | `1351` |  | `{`
| 124 | `1352` |  | `var request = new SftpReadDirRequest(ProtocolVersion,`
| 124 | `1353` |  | `NextRequestId,`
| 124 | `1354` |  | `handle,`
| 124 | `1355` |  | `response =>`
| 112 | `1356` |  | `{`
| 112 | `1357` |  | `result = response.Files;`
| 112 | `1358` |  | `_ = wait.Set();`
| 112 | `1359` |  | `},`
| 124 | `1360` |  | `response =>`
| 12 | `1361` |  | `{`
| 12 | `1362` |  | `if (response.StatusCode != StatusCodes.Eof)`
| 0 | `1363` |  | `{`
| 0 | `1364` |  | `exception = GetSftpException(response);`
| 0 | `1365` |  | `}`
| 124 | `1366` |  | ``
| 12 | `1367` |  | `_ = wait.Set();`
| 136 | `1368` |  | `});`
|  | `1369` |  | ``
| 124 | `1370` |  | `SendRequest(request);`
|  | `1371` |  | ``
| 124 | `1372` |  | `WaitOnHandle(wait, OperationTimeout);`
| 124 | `1373` |  | `}`
|  | `1374` |  | ``
|  | `1375` |  | `#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 124 | `1376` |  | `if (exception is not null)`
|  | `1377` |  | `#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 0 | `1378` |  | `{`
| 0 | `1379` |  | `throw exception;`
|  | `1380` |  | `}`
|  | `1381` |  | ``
| 124 | `1382` |  | `return result;`
| 124 | `1383` |  | `}`
|  | `1384` |  | ``
|  | `1385` |  | `/// <summary>`
|  | `1386` |  | `/// Performs a <c>SSH_FXP_READDIR</c> request.`
|  | `1387` |  | `/// </summary>`
|  | `1388` |  | `/// <param name="handle">The handle of the directory to read.</param>`
|  | `1389` |  | `/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>`
|  | `1390` |  | `/// <returns>`
|  | `1391` |  | `/// A task that represents the asynchronous <c>SSH_FXP_READDIR</c> request. The value of its`
|  | `1392` |  | `/// <see cref="Task{Task}.Result"/> contains a <see cref="Dictionary{TKey,TValue}"/> where the`
|  | `1393` |  | `/// <c>key</c> is the name of a file in the directory and the <c>value</c> is the <see cref="SftpFileAttributes"`
|  | `1394` |  | `/// of the file.`
|  | `1395` |  | `/// </returns>`
|  | `1396` |  | `public async Task<KeyValuePair<string, SftpFileAttributes>[]> RequestReadDirAsync(byte[] handle, CancellationTok`
| 4 | `1397` |  | `{`
| 4 | `1398` |  | `cancellationToken.ThrowIfCancellationRequested();`
|  | `1399` |  | ``
| 4 | `1400` |  | `var tcs = new TaskCompletionSource<KeyValuePair<string, SftpFileAttributes>[]>(TaskCreationOptions.RunContin`
|  | `1401` |  | ``
|  | `1402` |  | `#if NET || NETSTANDARD2_1_OR_GREATER`
| 4 | `1403` |  | `await using (cancellationToken.Register(s => ((TaskCompletionSource<KeyValuePair<string, SftpFileAttributes>`
|  | `1404` |  | `#else`
| 0 | `1405` |  | `using (cancellationToken.Register(s => ((TaskCompletionSource<KeyValuePair<string, SftpFileAttributes>[]>) s`
|  | `1406` |  | `#endif // NET || NETSTANDARD2_1_OR_GREATER`
| 4 | `1407` |  | `{`
| 4 | `1408` |  | `SendRequest(new SftpReadDirRequest(ProtocolVersion,`
| 4 | `1409` |  | `NextRequestId,`
| 4 | `1410` |  | `handle,`
| 2 | `1411` |  | `response => tcs.TrySetResult(response.Files),`
| 4 | `1412` |  | `response =>`
| 2 | `1413` |  | `{`
| 2 | `1414` |  | `if (response.StatusCode == StatusCodes.Eof)`
| 2 | `1415` |  | `{`
| 2 | `1416` |  | `_ = tcs.TrySetResult(null);`
| 2 | `1417` |  | `}`
| 4 | `1418` |  | `else`
| 0 | `1419` |  | `{`
| 0 | `1420` |  | `_ = tcs.TrySetException(GetSftpException(response));`
| 0 | `1421` |  | `}`
| 6 | `1422` |  | `}));`
|  | `1423` |  | ``
| 4 | `1424` |  | `return await tcs.Task.ConfigureAwait(false);`
|  | `1425` |  | `}`
| 4 | `1426` |  | `}`
|  | `1427` |  | ``
|  | `1428` |  | `/// <summary>`
|  | `1429` |  | `/// Performs SSH_FXP_REMOVE request.`
|  | `1430` |  | `/// </summary>`
|  | `1431` |  | `/// <param name="path">The path.</param>`
|  | `1432` |  | `public void RequestRemove(string path)`
| 162 | `1433` |  | `{`
| 162 | `1434` |  | `SshException exception = null;`
|  | `1435` |  | ``
| 162 | `1436` |  | `using (var wait = new AutoResetEvent(initialState: false))`
| 162 | `1437` |  | `{`
| 162 | `1438` |  | `var request = new SftpRemoveRequest(ProtocolVersion,`
| 162 | `1439` |  | `NextRequestId,`
| 162 | `1440` |  | `path,`
| 162 | `1441` |  | `_encoding,`
| 162 | `1442` |  | `response =>`
| 162 | `1443` |  | `{`
| 162 | `1444` |  | `exception = GetSftpException(response);`
| 162 | `1445` |  | `_ = wait.Set();`
| 324 | `1446` |  | `});`
|  | `1447` |  | ``
| 162 | `1448` |  | `SendRequest(request);`
|  | `1449` |  | ``
| 162 | `1450` |  | `WaitOnHandle(wait, OperationTimeout);`
| 162 | `1451` |  | `}`
|  | `1452` |  | ``
|  | `1453` |  | `#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 162 | `1454` |  | `if (exception is not null)`
|  | `1455` |  | `#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 0 | `1456` |  | `{`
| 0 | `1457` |  | `throw exception;`
|  | `1458` |  | `}`
| 162 | `1459` |  | `}`
|  | `1460` |  | ``
|  | `1461` |  | `/// <summary>`
|  | `1462` |  | `/// Asynchronously performs a <c>SSH_FXP_REMOVE</c> request.`
|  | `1463` |  | `/// </summary>`
|  | `1464` |  | `/// <param name="path">The path.</param>`
|  | `1465` |  | `/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>`
|  | `1466` |  | `/// <returns>`
|  | `1467` |  | `/// A task that represents the asynchronous <c>SSH_FXP_REMOVE</c> request.`
|  | `1468` |  | `/// </returns>`
|  | `1469` |  | `public async Task RequestRemoveAsync(string path, CancellationToken cancellationToken)`
| 0 | `1470` |  | `{`
| 0 | `1471` |  | `cancellationToken.ThrowIfCancellationRequested();`
|  | `1472` |  | ``
| 0 | `1473` |  | `var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);`
|  | `1474` |  | ``
|  | `1475` |  | `#if NET || NETSTANDARD2_1_OR_GREATER`
| 0 | `1476` |  | `await using (cancellationToken.Register(s => ((TaskCompletionSource<bool>) s).TrySetCanceled(cancellationTok`
|  | `1477` |  | `#else`
| 0 | `1478` |  | `using (cancellationToken.Register(s => ((TaskCompletionSource<bool>) s).TrySetCanceled(cancellationToken), t`
|  | `1479` |  | `#endif // NET || NETSTANDARD2_1_OR_GREATER`
| 0 | `1480` |  | `{`
| 0 | `1481` |  | `SendRequest(new SftpRemoveRequest(ProtocolVersion,`
| 0 | `1482` |  | `NextRequestId,`
| 0 | `1483` |  | `path,`
| 0 | `1484` |  | `_encoding,`
| 0 | `1485` |  | `response =>`
| 0 | `1486` |  | `{`
| 0 | `1487` |  | `if (response.StatusCode == StatusCodes.Ok)`
| 0 | `1488` |  | `{`
| 0 | `1489` |  | `_ = tcs.TrySetResult(true);`
| 0 | `1490` |  | `}`
| 0 | `1491` |  | `else`
| 0 | `1492` |  | `{`
| 0 | `1493` |  | `_ = tcs.TrySetException(GetSftpException(response));`
| 0 | `1494` |  | `}`
| 0 | `1495` |  | `}));`
|  | `1496` |  | ``
| 0 | `1497` |  | `_ = await tcs.Task.ConfigureAwait(false);`
| 0 | `1498` |  | `}`
| 0 | `1499` |  | `}`
|  | `1500` |  | ``
|  | `1501` |  | `/// <summary>`
|  | `1502` |  | `/// Performs SSH_FXP_MKDIR request.`
|  | `1503` |  | `/// </summary>`
|  | `1504` |  | `/// <param name="path">The path.</param>`
|  | `1505` |  | `public void RequestMkDir(string path)`
| 10037 | `1506` |  | `{`
| 10037 | `1507` |  | `SshException exception = null;`
|  | `1508` |  | ``
| 10037 | `1509` |  | `using (var wait = new AutoResetEvent(initialState: false))`
| 10037 | `1510` |  | `{`
| 10037 | `1511` |  | `var request = new SftpMkDirRequest(ProtocolVersion,`
| 10037 | `1512` |  | `NextRequestId,`
| 10037 | `1513` |  | `path,`
| 10037 | `1514` |  | `_encoding,`
| 10037 | `1515` |  | `response =>`
| 10037 | `1516` |  | `{`
| 10037 | `1517` |  | `exception = GetSftpException(response);`
| 10037 | `1518` |  | `_ = wait.Set();`
| 20074 | `1519` |  | `});`
|  | `1520` |  | ``
| 10037 | `1521` |  | `SendRequest(request);`
|  | `1522` |  | ``
| 10037 | `1523` |  | `WaitOnHandle(wait, OperationTimeout);`
| 10037 | `1524` |  | `}`
|  | `1525` |  | ``
|  | `1526` |  | `#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 10037 | `1527` |  | `if (exception is not null)`
|  | `1528` |  | `#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 3 | `1529` |  | `{`
| 3 | `1530` |  | `throw exception;`
|  | `1531` |  | `}`
| 10034 | `1532` |  | `}`
|  | `1533` |  | ``
|  | `1534` |  | `/// <summary>`
|  | `1535` |  | `/// Performs SSH_FXP_RMDIR request.`
|  | `1536` |  | `/// </summary>`
|  | `1537` |  | `/// <param name="path">The path.</param>`
|  | `1538` |  | `public void RequestRmDir(string path)`
| 38 | `1539` |  | `{`
| 38 | `1540` |  | `SshException exception = null;`
|  | `1541` |  | ``
| 38 | `1542` |  | `using (var wait = new AutoResetEvent(initialState: false))`
| 38 | `1543` |  | `{`
| 38 | `1544` |  | `var request = new SftpRmDirRequest(ProtocolVersion,`
| 38 | `1545` |  | `NextRequestId,`
| 38 | `1546` |  | `path,`
| 38 | `1547` |  | `_encoding,`
| 38 | `1548` |  | `response =>`
| 38 | `1549` |  | `{`
| 38 | `1550` |  | `exception = GetSftpException(response);`
| 38 | `1551` |  | `_ = wait.Set();`
| 76 | `1552` |  | `});`
|  | `1553` |  | ``
| 38 | `1554` |  | `SendRequest(request);`
|  | `1555` |  | ``
| 38 | `1556` |  | `WaitOnHandle(wait, OperationTimeout);`
| 38 | `1557` |  | `}`
|  | `1558` |  | ``
|  | `1559` |  | `#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 38 | `1560` |  | `if (exception is not null)`
|  | `1561` |  | `#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 2 | `1562` |  | `{`
| 2 | `1563` |  | `throw exception;`
|  | `1564` |  | `}`
| 36 | `1565` |  | `}`
|  | `1566` |  | ``
|  | `1567` |  | `/// <summary>`
|  | `1568` |  | `/// Performs SSH_FXP_REALPATH request.`
|  | `1569` |  | `/// </summary>`
|  | `1570` |  | `/// <param name="path">The path.</param>`
|  | `1571` |  | `/// <param name="nullOnError">if set to <see langword="true"/> returns null instead of throwing an exception.</p`
|  | `1572` |  | `/// <returns>`
|  | `1573` |  | `/// The absolute path.`
|  | `1574` |  | `/// </returns>`
|  | `1575` |  | `internal KeyValuePair<string, SftpFileAttributes>[] RequestRealPath(string path, bool nullOnError = false)`
| 11615 | `1576` |  | `{`
| 11615 | `1577` |  | `SshException exception = null;`
|  | `1578` |  | ``
| 11615 | `1579` |  | `KeyValuePair<string, SftpFileAttributes>[] result = null;`
|  | `1580` |  | ``
| 11615 | `1581` |  | `using (var wait = new AutoResetEvent(initialState: false))`
| 11615 | `1582` |  | `{`
| 11615 | `1583` |  | `var request = new SftpRealPathRequest(ProtocolVersion,`
| 11615 | `1584` |  | `NextRequestId,`
| 11615 | `1585` |  | `path,`
| 11615 | `1586` |  | `_encoding,`
| 11615 | `1587` |  | `response =>`
| 11515 | `1588` |  | `{`
| 11515 | `1589` |  | `result = response.Files;`
| 11515 | `1590` |  | `_ = wait.Set();`
| 11515 | `1591` |  | `},`
| 11615 | `1592` |  | `response =>`
| 97 | `1593` |  | `{`
| 97 | `1594` |  | `exception = GetSftpException(response);`
| 97 | `1595` |  | `_ = wait.Set();`
| 11712 | `1596` |  | `});`
|  | `1597` |  | ``
| 11615 | `1598` |  | `SendRequest(request);`
|  | `1599` |  | ``
| 11613 | `1600` |  | `WaitOnHandle(wait, OperationTimeout);`
| 11612 | `1601` |  | `}`
|  | `1602` |  | ``
|  | `1603` |  | `#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 11612 | `1604` |  | `if (!nullOnError && exception is not null)`
|  | `1605` |  | `#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 0 | `1606` |  | `{`
| 0 | `1607` |  | `throw exception;`
|  | `1608` |  | `}`
|  | `1609` |  | ``
| 11612 | `1610` |  | `return result;`
| 11612 | `1611` |  | `}`
|  | `1612` |  | ``
|  | `1613` |  | `internal async Task<KeyValuePair<string, SftpFileAttributes>[]> RequestRealPathAsync(string path, bool nullOnErr`
| 4 | `1614` |  | `{`
| 4 | `1615` |  | `cancellationToken.ThrowIfCancellationRequested();`
|  | `1616` |  | ``
| 4 | `1617` |  | `var tcs = new TaskCompletionSource<KeyValuePair<string, SftpFileAttributes>[]>(TaskCreationOptions.RunContin`
|  | `1618` |  | ``
|  | `1619` |  | `#if NET || NETSTANDARD2_1_OR_GREATER`
| 4 | `1620` |  | `await using (cancellationToken.Register(s => ((TaskCompletionSource<KeyValuePair<string, SftpFileAttributes>`
|  | `1621` |  | `#else`
| 0 | `1622` |  | `using (cancellationToken.Register(s => ((TaskCompletionSource<KeyValuePair<string, SftpFileAttributes>[]>) s`
|  | `1623` |  | `#endif // NET || NETSTANDARD2_1_OR_GREATER`
| 4 | `1624` |  | `{`
| 4 | `1625` |  | `SendRequest(new SftpRealPathRequest(ProtocolVersion,`
| 4 | `1626` |  | `NextRequestId,`
| 4 | `1627` |  | `path,`
| 4 | `1628` |  | `_encoding,`
| 4 | `1629` |  | `response => tcs.TrySetResult(response.Files),`
| 4 | `1630` |  | `response =>`
| 0 | `1631` |  | `{`
| 0 | `1632` |  | `if (nullOnError)`
| 0 | `1633` |  | `{`
| 0 | `1634` |  | `_ = tcs.TrySetResult(null);`
| 0 | `1635` |  | `}`
| 4 | `1636` |  | `else`
| 0 | `1637` |  | `{`
| 0 | `1638` |  | `_ = tcs.TrySetException(GetSftpException(response));`
| 0 | `1639` |  | `}`
| 4 | `1640` |  | `}));`
|  | `1641` |  | ``
| 4 | `1642` |  | `return await tcs.Task.ConfigureAwait(false);`
|  | `1643` |  | `}`
| 4 | `1644` |  | `}`
|  | `1645` |  | ``
|  | `1646` |  | `/// <summary>`
|  | `1647` |  | `/// Performs SSH_FXP_REALPATH request.`
|  | `1648` |  | `/// </summary>`
|  | `1649` |  | `/// <param name="path">The path.</param>`
|  | `1650` |  | `/// <param name="callback">The <see cref="AsyncCallback"/> delegate that is executed when <see cref="BeginRealPa`
|  | `1651` |  | `/// <param name="state">An object that contains any additional user-defined data.</param>`
|  | `1652` |  | `/// <returns>`
|  | `1653` |  | `/// A <see cref="SftpRealPathAsyncResult"/> that represents the asynchronous call.`
|  | `1654` |  | `/// </returns>`
|  | `1655` |  | `public SftpRealPathAsyncResult BeginRealPath(string path, AsyncCallback callback, object state)`
| 0 | `1656` |  | `{`
| 0 | `1657` |  | `var asyncResult = new SftpRealPathAsyncResult(callback, state);`
|  | `1658` |  | ``
| 0 | `1659` |  | `var request = new SftpRealPathRequest(ProtocolVersion,`
| 0 | `1660` |  | `NextRequestId,`
| 0 | `1661` |  | `path,`
| 0 | `1662` |  | `_encoding,`
| 0 | `1663` |  | `response => asyncResult.SetAsCompleted(response.Files[0].Key, complete`
| 0 | `1664` |  | `response => asyncResult.SetAsCompleted(GetSftpException(response), com`
| 0 | `1665` |  | `SendRequest(request);`
|  | `1666` |  | ``
| 0 | `1667` |  | `return asyncResult;`
| 0 | `1668` |  | `}`
|  | `1669` |  | ``
|  | `1670` |  | `/// <summary>`
|  | `1671` |  | `/// Handles the end of an asynchronous SSH_FXP_REALPATH request.`
|  | `1672` |  | `/// </summary>`
|  | `1673` |  | `/// <param name="asyncResult">An <see cref="SftpRealPathAsyncResult"/> that represents an asynchronous call.</pa`
|  | `1674` |  | `/// <returns>`
|  | `1675` |  | `/// The absolute path.`
|  | `1676` |  | `/// </returns>`
|  | `1677` |  | `/// <exception cref="ArgumentNullException"><paramref name="asyncResult"/> is <see langword="null"/>.</exception`
|  | `1678` |  | `public string EndRealPath(SftpRealPathAsyncResult asyncResult)`
| 0 | `1679` |  | `{`
| 0 | `1680` |  | `if (asyncResult is null)`
| 0 | `1681` |  | `{`
| 0 | `1682` |  | `throw new ArgumentNullException(nameof(asyncResult));`
|  | `1683` |  | `}`
|  | `1684` |  | ``
| 0 | `1685` |  | `if (asyncResult.EndInvokeCalled)`
| 0 | `1686` |  | `{`
| 0 | `1687` |  | `throw new InvalidOperationException("EndRealPath has already been called.");`
|  | `1688` |  | `}`
|  | `1689` |  | ``
| 0 | `1690` |  | `if (asyncResult.IsCompleted)`
| 0 | `1691` |  | `{`
| 0 | `1692` |  | `return asyncResult.EndInvoke();`
|  | `1693` |  | `}`
|  | `1694` |  | ``
| 0 | `1695` |  | `using (var waitHandle = asyncResult.AsyncWaitHandle)`
| 0 | `1696` |  | `{`
| 0 | `1697` |  | `WaitOnHandle(waitHandle, OperationTimeout);`
| 0 | `1698` |  | `return asyncResult.EndInvoke();`
|  | `1699` |  | `}`
| 0 | `1700` |  | `}`
|  | `1701` |  | ``
|  | `1702` |  | `/// <summary>`
|  | `1703` |  | `/// Performs SSH_FXP_STAT request.`
|  | `1704` |  | `/// </summary>`
|  | `1705` |  | `/// <param name="path">The path.</param>`
|  | `1706` |  | `/// <param name="nullOnError">if set to <see langword="true"/> returns null instead of throwing an exception.</p`
|  | `1707` |  | `/// <returns>`
|  | `1708` |  | `/// File attributes.`
|  | `1709` |  | `/// </returns>`
|  | `1710` |  | `public SftpFileAttributes RequestStat(string path, bool nullOnError = false)`
| 0 | `1711` |  | `{`
| 0 | `1712` |  | `SshException exception = null;`
|  | `1713` |  | ``
| 0 | `1714` |  | `SftpFileAttributes attributes = null;`
|  | `1715` |  | ``
| 0 | `1716` |  | `using (var wait = new AutoResetEvent(initialState: false))`
| 0 | `1717` |  | `{`
| 0 | `1718` |  | `var request = new SftpStatRequest(ProtocolVersion,`
| 0 | `1719` |  | `NextRequestId,`
| 0 | `1720` |  | `path,`
| 0 | `1721` |  | `_encoding,`
| 0 | `1722` |  | `response =>`
| 0 | `1723` |  | `{`
| 0 | `1724` |  | `attributes = response.Attributes;`
| 0 | `1725` |  | `_ = wait.Set();`
| 0 | `1726` |  | `},`
| 0 | `1727` |  | `response =>`
| 0 | `1728` |  | `{`
| 0 | `1729` |  | `exception = GetSftpException(response);`
| 0 | `1730` |  | `_ = wait.Set();`
| 0 | `1731` |  | `});`
|  | `1732` |  | ``
| 0 | `1733` |  | `SendRequest(request);`
|  | `1734` |  | ``
| 0 | `1735` |  | `WaitOnHandle(wait, OperationTimeout);`
| 0 | `1736` |  | `}`
|  | `1737` |  | ``
|  | `1738` |  | `#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 0 | `1739` |  | `if (!nullOnError && exception is not null)`
|  | `1740` |  | `#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 0 | `1741` |  | `{`
| 0 | `1742` |  | `throw exception;`
|  | `1743` |  | `}`
|  | `1744` |  | ``
| 0 | `1745` |  | `return attributes;`
| 0 | `1746` |  | `}`
|  | `1747` |  | ``
|  | `1748` |  | `/// <summary>`
|  | `1749` |  | `/// Performs SSH_FXP_STAT request.`
|  | `1750` |  | `/// </summary>`
|  | `1751` |  | `/// <param name="path">The path.</param>`
|  | `1752` |  | `/// <param name="callback">The <see cref="AsyncCallback"/> delegate that is executed when <see cref="BeginStat(s`
|  | `1753` |  | `/// <param name="state">An object that contains any additional user-defined data.</param>`
|  | `1754` |  | `/// <returns>`
|  | `1755` |  | `/// A <see cref="SFtpStatAsyncResult"/> that represents the asynchronous call.`
|  | `1756` |  | `/// </returns>`
|  | `1757` |  | `public SFtpStatAsyncResult BeginStat(string path, AsyncCallback callback, object state)`
| 0 | `1758` |  | `{`
| 0 | `1759` |  | `var asyncResult = new SFtpStatAsyncResult(callback, state);`
|  | `1760` |  | ``
| 0 | `1761` |  | `var request = new SftpStatRequest(ProtocolVersion,`
| 0 | `1762` |  | `NextRequestId,`
| 0 | `1763` |  | `path,`
| 0 | `1764` |  | `_encoding,`
| 0 | `1765` |  | `response => asyncResult.SetAsCompleted(response.Attributes, completedSynch`
| 0 | `1766` |  | `response => asyncResult.SetAsCompleted(GetSftpException(response), complet`
| 0 | `1767` |  | `SendRequest(request);`
|  | `1768` |  | ``
| 0 | `1769` |  | `return asyncResult;`
| 0 | `1770` |  | `}`
|  | `1771` |  | ``
|  | `1772` |  | `/// <summary>`
|  | `1773` |  | `/// Handles the end of an asynchronous stat.`
|  | `1774` |  | `/// </summary>`
|  | `1775` |  | `/// <param name="asyncResult">An <see cref="SFtpStatAsyncResult"/> that represents an asynchronous call.</param>`
|  | `1776` |  | `/// <returns>`
|  | `1777` |  | `/// The file attributes.`
|  | `1778` |  | `/// </returns>`
|  | `1779` |  | `/// <exception cref="ArgumentNullException"><paramref name="asyncResult"/> is <see langword="null"/>.</exception`
|  | `1780` |  | `public SftpFileAttributes EndStat(SFtpStatAsyncResult asyncResult)`
| 0 | `1781` |  | `{`
| 0 | `1782` |  | `if (asyncResult is null)`
| 0 | `1783` |  | `{`
| 0 | `1784` |  | `throw new ArgumentNullException(nameof(asyncResult));`
|  | `1785` |  | `}`
|  | `1786` |  | ``
| 0 | `1787` |  | `if (asyncResult.EndInvokeCalled)`
| 0 | `1788` |  | `{`
| 0 | `1789` |  | `throw new InvalidOperationException("EndStat has already been called.");`
|  | `1790` |  | `}`
|  | `1791` |  | ``
| 0 | `1792` |  | `if (asyncResult.IsCompleted)`
| 0 | `1793` |  | `{`
| 0 | `1794` |  | `return asyncResult.EndInvoke();`
|  | `1795` |  | `}`
|  | `1796` |  | ``
| 0 | `1797` |  | `using (var waitHandle = asyncResult.AsyncWaitHandle)`
| 0 | `1798` |  | `{`
| 0 | `1799` |  | `WaitOnHandle(waitHandle, OperationTimeout);`
| 0 | `1800` |  | `return asyncResult.EndInvoke();`
|  | `1801` |  | `}`
| 0 | `1802` |  | `}`
|  | `1803` |  | ``
|  | `1804` |  | `/// <summary>`
|  | `1805` |  | `/// Performs SSH_FXP_RENAME request.`
|  | `1806` |  | `/// </summary>`
|  | `1807` |  | `/// <param name="oldPath">The old path.</param>`
|  | `1808` |  | `/// <param name="newPath">The new path.</param>`
|  | `1809` |  | `public void RequestRename(string oldPath, string newPath)`
| 2 | `1810` |  | `{`
| 2 | `1811` |  | `if (ProtocolVersion < 2)`
| 0 | `1812` |  | `{`
| 0 | `1813` |  | `throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_RENAME operation is n`
|  | `1814` |  | `}`
|  | `1815` |  | ``
| 2 | `1816` |  | `SshException exception = null;`
|  | `1817` |  | ``
| 2 | `1818` |  | `using (var wait = new AutoResetEvent(initialState: false))`
| 2 | `1819` |  | `{`
| 2 | `1820` |  | `var request = new SftpRenameRequest(ProtocolVersion,`
| 2 | `1821` |  | `NextRequestId,`
| 2 | `1822` |  | `oldPath,`
| 2 | `1823` |  | `newPath,`
| 2 | `1824` |  | `_encoding,`
| 2 | `1825` |  | `response =>`
| 2 | `1826` |  | `{`
| 2 | `1827` |  | `exception = GetSftpException(response);`
| 2 | `1828` |  | `_ = wait.Set();`
| 4 | `1829` |  | `});`
|  | `1830` |  | ``
| 2 | `1831` |  | `SendRequest(request);`
|  | `1832` |  | ``
| 2 | `1833` |  | `WaitOnHandle(wait, OperationTimeout);`
| 2 | `1834` |  | `}`
|  | `1835` |  | ``
|  | `1836` |  | `#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 2 | `1837` |  | `if (exception is not null)`
|  | `1838` |  | `#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 0 | `1839` |  | `{`
| 0 | `1840` |  | `throw exception;`
|  | `1841` |  | `}`
| 2 | `1842` |  | `}`
|  | `1843` |  | ``
|  | `1844` |  | `/// <summary>`
|  | `1845` |  | `/// Asynchronously performs a <c>SSH_FXP_RENAME</c> request.`
|  | `1846` |  | `/// </summary>`
|  | `1847` |  | `/// <param name="oldPath">The old path.</param>`
|  | `1848` |  | `/// <param name="newPath">The new path.</param>`
|  | `1849` |  | `/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>`
|  | `1850` |  | `/// <returns>`
|  | `1851` |  | `/// A task that represents the asynchronous <c>SSH_FXP_RENAME</c> request.`
|  | `1852` |  | `/// </returns>`
|  | `1853` |  | `public async Task RequestRenameAsync(string oldPath, string newPath, CancellationToken cancellationToken)`
| 1 | `1854` |  | `{`
| 1 | `1855` |  | `cancellationToken.ThrowIfCancellationRequested();`
|  | `1856` |  | ``
| 1 | `1857` |  | `var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);`
|  | `1858` |  | ``
|  | `1859` |  | `#if NET || NETSTANDARD2_1_OR_GREATER`
| 1 | `1860` |  | `await using (cancellationToken.Register(s => ((TaskCompletionSource<bool>) s).TrySetCanceled(cancellationTok`
|  | `1861` |  | `#else`
| 0 | `1862` |  | `using (cancellationToken.Register(s => ((TaskCompletionSource<bool>) s).TrySetCanceled(cancellationToken), t`
|  | `1863` |  | `#endif // NET || NETSTANDARD2_1_OR_GREATER`
| 1 | `1864` |  | `{`
| 1 | `1865` |  | `SendRequest(new SftpRenameRequest(ProtocolVersion,`
| 1 | `1866` |  | `NextRequestId,`
| 1 | `1867` |  | `oldPath,`
| 1 | `1868` |  | `newPath,`
| 1 | `1869` |  | `_encoding,`
| 1 | `1870` |  | `response =>`
| 1 | `1871` |  | `{`
| 1 | `1872` |  | `if (response.StatusCode == StatusCodes.Ok)`
| 1 | `1873` |  | `{`
| 1 | `1874` |  | `_ = tcs.TrySetResult(true);`
| 1 | `1875` |  | `}`
| 1 | `1876` |  | `else`
| 0 | `1877` |  | `{`
| 0 | `1878` |  | `_ = tcs.TrySetException(GetSftpException(response));`
| 0 | `1879` |  | `}`
| 2 | `1880` |  | `}));`
|  | `1881` |  | ``
| 1 | `1882` |  | `_ = await tcs.Task.ConfigureAwait(false);`
| 1 | `1883` |  | `}`
| 1 | `1884` |  | `}`
|  | `1885` |  | ``
|  | `1886` |  | `/// <summary>`
|  | `1887` |  | `/// Performs SSH_FXP_READLINK request.`
|  | `1888` |  | `/// </summary>`
|  | `1889` |  | `/// <param name="path">The path.</param>`
|  | `1890` |  | `/// <param name="nullOnError">if set to <see langword="true"/> returns <see langword="null"/> instead of throwin`
|  | `1891` |  | `/// <returns>`
|  | `1892` |  | `/// An array of <see cref="KeyValuePair{TKey,TValue}"/> where the <c>key</c> is the name of`
|  | `1893` |  | `/// a file and the <c>value</c> is the <see cref="SftpFileAttributes"/> of the file.`
|  | `1894` |  | `/// </returns>`
|  | `1895` |  | `internal KeyValuePair<string, SftpFileAttributes>[] RequestReadLink(string path, bool nullOnError = false)`
| 0 | `1896` |  | `{`
| 0 | `1897` |  | `if (ProtocolVersion < 3)`
| 0 | `1898` |  | `{`
| 0 | `1899` |  | `throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_READLINK operation is`
|  | `1900` |  | `}`
|  | `1901` |  | ``
| 0 | `1902` |  | `SshException exception = null;`
|  | `1903` |  | ``
| 0 | `1904` |  | `KeyValuePair<string, SftpFileAttributes>[] result = null;`
|  | `1905` |  | ``
| 0 | `1906` |  | `using (var wait = new AutoResetEvent(initialState: false))`
| 0 | `1907` |  | `{`
| 0 | `1908` |  | `var request = new SftpReadLinkRequest(ProtocolVersion,`
| 0 | `1909` |  | `NextRequestId,`
| 0 | `1910` |  | `path,`
| 0 | `1911` |  | `_encoding,`
| 0 | `1912` |  | `response =>`
| 0 | `1913` |  | `{`
| 0 | `1914` |  | `result = response.Files;`
| 0 | `1915` |  | `_ = wait.Set();`
| 0 | `1916` |  | `},`
| 0 | `1917` |  | `response =>`
| 0 | `1918` |  | `{`
| 0 | `1919` |  | `exception = GetSftpException(response);`
| 0 | `1920` |  | `_ = wait.Set();`
| 0 | `1921` |  | `});`
|  | `1922` |  | ``
| 0 | `1923` |  | `SendRequest(request);`
|  | `1924` |  | ``
| 0 | `1925` |  | `WaitOnHandle(wait, OperationTimeout);`
| 0 | `1926` |  | `}`
|  | `1927` |  | ``
|  | `1928` |  | `#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 0 | `1929` |  | `if (!nullOnError && exception is not null)`
|  | `1930` |  | `#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 0 | `1931` |  | `{`
| 0 | `1932` |  | `throw exception;`
|  | `1933` |  | `}`
|  | `1934` |  | ``
| 0 | `1935` |  | `return result;`
| 0 | `1936` |  | `}`
|  | `1937` |  | ``
|  | `1938` |  | `/// <summary>`
|  | `1939` |  | `/// Performs SSH_FXP_SYMLINK request.`
|  | `1940` |  | `/// </summary>`
|  | `1941` |  | `/// <param name="linkpath">The linkpath.</param>`
|  | `1942` |  | `/// <param name="targetpath">The targetpath.</param>`
|  | `1943` |  | `public void RequestSymLink(string linkpath, string targetpath)`
| 0 | `1944` |  | `{`
| 0 | `1945` |  | `if (ProtocolVersion < 3)`
| 0 | `1946` |  | `{`
| 0 | `1947` |  | `throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_SYMLINK operation is`
|  | `1948` |  | `}`
|  | `1949` |  | ``
| 0 | `1950` |  | `SshException exception = null;`
|  | `1951` |  | ``
| 0 | `1952` |  | `using (var wait = new AutoResetEvent(initialState: false))`
| 0 | `1953` |  | `{`
| 0 | `1954` |  | `var request = new SftpSymLinkRequest(ProtocolVersion,`
| 0 | `1955` |  | `NextRequestId,`
| 0 | `1956` |  | `linkpath,`
| 0 | `1957` |  | `targetpath,`
| 0 | `1958` |  | `_encoding,`
| 0 | `1959` |  | `response =>`
| 0 | `1960` |  | `{`
| 0 | `1961` |  | `exception = GetSftpException(response);`
| 0 | `1962` |  | `_ = wait.Set();`
| 0 | `1963` |  | `});`
|  | `1964` |  | ``
| 0 | `1965` |  | `SendRequest(request);`
|  | `1966` |  | ``
| 0 | `1967` |  | `WaitOnHandle(wait, OperationTimeout);`
| 0 | `1968` |  | `}`
|  | `1969` |  | ``
|  | `1970` |  | `#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 0 | `1971` |  | `if (exception is not null)`
|  | `1972` |  | `#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 0 | `1973` |  | `{`
| 0 | `1974` |  | `throw exception;`
|  | `1975` |  | `}`
| 0 | `1976` |  | `}`
|  | `1977` |  | ``
|  | `1978` |  | `/// <summary>`
|  | `1979` |  | `/// Performs posix-rename@openssh.com extended request.`
|  | `1980` |  | `/// </summary>`
|  | `1981` |  | `/// <param name="oldPath">The old path.</param>`
|  | `1982` |  | `/// <param name="newPath">The new path.</param>`
|  | `1983` |  | `public void RequestPosixRename(string oldPath, string newPath)`
| 0 | `1984` |  | `{`
| 0 | `1985` |  | `if (ProtocolVersion < 3)`
| 0 | `1986` |  | `{`
| 0 | `1987` |  | `throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_EXTENDED operation is`
|  | `1988` |  | `}`
|  | `1989` |  | ``
| 0 | `1990` |  | `SshException exception = null;`
|  | `1991` |  | ``
| 0 | `1992` |  | `using (var wait = new AutoResetEvent(initialState: false))`
| 0 | `1993` |  | `{`
| 0 | `1994` |  | `var request = new PosixRenameRequest(ProtocolVersion,`
| 0 | `1995` |  | `NextRequestId,`
| 0 | `1996` |  | `oldPath,`
| 0 | `1997` |  | `newPath,`
| 0 | `1998` |  | `_encoding,`
| 0 | `1999` |  | `response =>`
| 0 | `2000` |  | `{`
| 0 | `2001` |  | `exception = GetSftpException(response);`
| 0 | `2002` |  | `_ = wait.Set();`
| 0 | `2003` |  | `});`
|  | `2004` |  | ``
| 0 | `2005` |  | `if (!_supportedExtensions.ContainsKey(request.Name))`
| 0 | `2006` |  | `{`
| 0 | `2007` |  | `throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Extension method {0} curr`
|  | `2008` |  | `}`
|  | `2009` |  | ``
| 0 | `2010` |  | `SendRequest(request);`
|  | `2011` |  | ``
| 0 | `2012` |  | `WaitOnHandle(wait, OperationTimeout);`
| 0 | `2013` |  | `}`
|  | `2014` |  | ``
|  | `2015` |  | `#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 0 | `2016` |  | `if (exception is not null)`
|  | `2017` |  | `#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 0 | `2018` |  | `{`
| 0 | `2019` |  | `throw exception;`
|  | `2020` |  | `}`
| 0 | `2021` |  | `}`
|  | `2022` |  | ``
|  | `2023` |  | `/// <summary>`
|  | `2024` |  | `/// Performs statvfs@openssh.com extended request.`
|  | `2025` |  | `/// </summary>`
|  | `2026` |  | `/// <param name="path">The path.</param>`
|  | `2027` |  | `/// <param name="nullOnError">if set to <see langword="true"/> [null on error].</param>`
|  | `2028` |  | `/// <returns>`
|  | `2029` |  | `/// A <see cref="SftpFileSytemInformation"/> for the specified path.`
|  | `2030` |  | `/// </returns>`
|  | `2031` |  | `public SftpFileSytemInformation RequestStatVfs(string path, bool nullOnError = false)`
| 6 | `2032` |  | `{`
| 6 | `2033` |  | `if (ProtocolVersion < 3)`
| 0 | `2034` |  | `{`
| 0 | `2035` |  | `throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_EXTENDED operation is`
|  | `2036` |  | `}`
|  | `2037` |  | ``
| 6 | `2038` |  | `SshException exception = null;`
|  | `2039` |  | ``
| 6 | `2040` |  | `SftpFileSytemInformation information = null;`
|  | `2041` |  | ``
| 6 | `2042` |  | `using (var wait = new AutoResetEvent(initialState: false))`
| 6 | `2043` |  | `{`
| 6 | `2044` |  | `var request = new StatVfsRequest(ProtocolVersion,`
| 6 | `2045` |  | `NextRequestId,`
| 6 | `2046` |  | `path,`
| 6 | `2047` |  | `_encoding,`
| 6 | `2048` |  | `response =>`
| 6 | `2049` |  | `{`
| 6 | `2050` |  | `information = response.GetReply<StatVfsReplyInfo>().Information;`
| 6 | `2051` |  | `_ = wait.Set();`
| 6 | `2052` |  | `},`
| 6 | `2053` |  | `response =>`
| 0 | `2054` |  | `{`
| 0 | `2055` |  | `exception = GetSftpException(response);`
| 0 | `2056` |  | `_ = wait.Set();`
| 6 | `2057` |  | `});`
|  | `2058` |  | ``
| 6 | `2059` |  | `if (!_supportedExtensions.ContainsKey(request.Name))`
| 0 | `2060` |  | `{`
| 0 | `2061` |  | `throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Extension method {0} curr`
|  | `2062` |  | `}`
|  | `2063` |  | ``
| 6 | `2064` |  | `SendRequest(request);`
|  | `2065` |  | ``
| 6 | `2066` |  | `WaitOnHandle(wait, OperationTimeout);`
| 6 | `2067` |  | `}`
|  | `2068` |  | ``
|  | `2069` |  | `#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 6 | `2070` |  | `if (!nullOnError && exception is not null)`
|  | `2071` |  | `#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 0 | `2072` |  | `{`
| 0 | `2073` |  | `throw exception;`
|  | `2074` |  | `}`
|  | `2075` |  | ``
| 6 | `2076` |  | `return information;`
| 6 | `2077` |  | `}`
|  | `2078` |  | ``
|  | `2079` |  | `/// <summary>`
|  | `2080` |  | `/// Asynchronously performs a <c>statvfs@openssh.com</c> extended request.`
|  | `2081` |  | `/// </summary>`
|  | `2082` |  | `/// <param name="path">The path.</param>`
|  | `2083` |  | `/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>`
|  | `2084` |  | `/// <returns>`
|  | `2085` |  | `/// A task that represents the <c>statvfs@openssh.com</c> extended request. The value of its`
|  | `2086` |  | `/// <see cref="Task{Task}.Result"/> contains the file system information for the specified`
|  | `2087` |  | `/// path.`
|  | `2088` |  | `/// </returns>`
|  | `2089` |  | `public async Task<SftpFileSytemInformation> RequestStatVfsAsync(string path, CancellationToken cancellationToken`
| 0 | `2090` |  | `{`
| 0 | `2091` |  | `if (ProtocolVersion < 3)`
| 0 | `2092` |  | `{`
| 0 | `2093` |  | `throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_EXTENDED operation is`
|  | `2094` |  | `}`
|  | `2095` |  | ``
| 0 | `2096` |  | `cancellationToken.ThrowIfCancellationRequested();`
|  | `2097` |  | ``
| 0 | `2098` |  | `var tcs = new TaskCompletionSource<SftpFileSytemInformation>(TaskCreationOptions.RunContinuationsAsynchronou`
|  | `2099` |  | ``
|  | `2100` |  | `#if NET || NETSTANDARD2_1_OR_GREATER`
| 0 | `2101` |  | `await using (cancellationToken.Register(s => ((TaskCompletionSource<SftpFileSytemInformation>) s).TrySetCanc`
|  | `2102` |  | `#else`
| 0 | `2103` |  | `using (cancellationToken.Register(s => ((TaskCompletionSource<SftpFileSytemInformation>) s).TrySetCanceled(c`
|  | `2104` |  | `#endif // NET || NETSTANDARD2_1_OR_GREATER`
| 0 | `2105` |  | `{`
| 0 | `2106` |  | `SendRequest(new StatVfsRequest(ProtocolVersion,`
| 0 | `2107` |  | `NextRequestId,`
| 0 | `2108` |  | `path,`
| 0 | `2109` |  | `_encoding,`
| 0 | `2110` |  | `response => tcs.TrySetResult(response.GetReply<StatVfsReplyInfo>().Inform`
| 0 | `2111` |  | `response => tcs.TrySetException(GetSftpException(response))));`
|  | `2112` |  | ``
| 0 | `2113` |  | `return await tcs.Task.ConfigureAwait(false);`
|  | `2114` |  | `}`
| 0 | `2115` |  | `}`
|  | `2116` |  | ``
|  | `2117` |  | `/// <summary>`
|  | `2118` |  | `/// Performs fstatvfs@openssh.com extended request.`
|  | `2119` |  | `/// </summary>`
|  | `2120` |  | `/// <param name="handle">The file handle.</param>`
|  | `2121` |  | `/// <param name="nullOnError">if set to <see langword="true"/> [null on error].</param>`
|  | `2122` |  | `/// <returns>`
|  | `2123` |  | `/// A <see cref="SftpFileSytemInformation"/> for the specified path.`
|  | `2124` |  | `/// </returns>`
|  | `2125` |  | `/// <exception cref="NotSupportedException">This operation is not supported for the current SFTP protocol versio`
|  | `2126` |  | `internal SftpFileSytemInformation RequestFStatVfs(byte[] handle, bool nullOnError = false)`
| 0 | `2127` |  | `{`
| 0 | `2128` |  | `if (ProtocolVersion < 3)`
| 0 | `2129` |  | `{`
| 0 | `2130` |  | `throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_EXTENDED operation is`
|  | `2131` |  | `}`
|  | `2132` |  | ``
| 0 | `2133` |  | `SshException exception = null;`
|  | `2134` |  | ``
| 0 | `2135` |  | `SftpFileSytemInformation information = null;`
|  | `2136` |  | ``
| 0 | `2137` |  | `using (var wait = new AutoResetEvent(initialState: false))`
| 0 | `2138` |  | `{`
| 0 | `2139` |  | `var request = new FStatVfsRequest(ProtocolVersion,`
| 0 | `2140` |  | `NextRequestId,`
| 0 | `2141` |  | `handle,`
| 0 | `2142` |  | `response =>`
| 0 | `2143` |  | `{`
| 0 | `2144` |  | `information = response.GetReply<StatVfsReplyInfo>().Information;`
| 0 | `2145` |  | `_ = wait.Set();`
| 0 | `2146` |  | `},`
| 0 | `2147` |  | `response =>`
| 0 | `2148` |  | `{`
| 0 | `2149` |  | `exception = GetSftpException(response);`
| 0 | `2150` |  | `_ = wait.Set();`
| 0 | `2151` |  | `});`
|  | `2152` |  | ``
| 0 | `2153` |  | `if (!_supportedExtensions.ContainsKey(request.Name))`
| 0 | `2154` |  | `{`
| 0 | `2155` |  | `throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Extension method {0} curr`
|  | `2156` |  | `}`
|  | `2157` |  | ``
| 0 | `2158` |  | `SendRequest(request);`
|  | `2159` |  | ``
| 0 | `2160` |  | `WaitOnHandle(wait, OperationTimeout);`
| 0 | `2161` |  | `}`
|  | `2162` |  | ``
|  | `2163` |  | `#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 0 | `2164` |  | `if (!nullOnError && exception is not null)`
|  | `2165` |  | `#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 0 | `2166` |  | `{`
| 0 | `2167` |  | `throw exception;`
|  | `2168` |  | `}`
|  | `2169` |  | ``
| 0 | `2170` |  | `return information;`
| 0 | `2171` |  | `}`
|  | `2172` |  | ``
|  | `2173` |  | `/// <summary>`
|  | `2174` |  | `/// Performs hardlink@openssh.com extended request.`
|  | `2175` |  | `/// </summary>`
|  | `2176` |  | `/// <param name="oldPath">The old path.</param>`
|  | `2177` |  | `/// <param name="newPath">The new path.</param>`
|  | `2178` |  | `internal void HardLink(string oldPath, string newPath)`
| 0 | `2179` |  | `{`
| 0 | `2180` |  | `if (ProtocolVersion < 3)`
| 0 | `2181` |  | `{`
| 0 | `2182` |  | `throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_EXTENDED operation is`
|  | `2183` |  | `}`
|  | `2184` |  | ``
| 0 | `2185` |  | `SshException exception = null;`
|  | `2186` |  | ``
| 0 | `2187` |  | `using (var wait = new AutoResetEvent(initialState: false))`
| 0 | `2188` |  | `{`
| 0 | `2189` |  | `var request = new HardLinkRequest(ProtocolVersion,`
| 0 | `2190` |  | `NextRequestId,`
| 0 | `2191` |  | `oldPath,`
| 0 | `2192` |  | `newPath,`
| 0 | `2193` |  | `response =>`
| 0 | `2194` |  | `{`
| 0 | `2195` |  | `exception = GetSftpException(response);`
| 0 | `2196` |  | `_ = wait.Set();`
| 0 | `2197` |  | `});`
|  | `2198` |  | ``
| 0 | `2199` |  | `if (!_supportedExtensions.ContainsKey(request.Name))`
| 0 | `2200` |  | `{`
| 0 | `2201` |  | `throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Extension method {0} curr`
|  | `2202` |  | `}`
|  | `2203` |  | ``
| 0 | `2204` |  | `SendRequest(request);`
|  | `2205` |  | ``
| 0 | `2206` |  | `WaitOnHandle(wait, OperationTimeout);`
| 0 | `2207` |  | `}`
|  | `2208` |  | ``
|  | `2209` |  | `#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 0 | `2210` |  | `if (exception is not null)`
|  | `2211` |  | `#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed`
| 0 | `2212` |  | `{`
| 0 | `2213` |  | `throw exception;`
|  | `2214` |  | `}`
| 0 | `2215` |  | `}`
|  | `2216` |  | ``
|  | `2217` |  | `/// <summary>`
|  | `2218` |  | `/// Calculates the optimal size of the buffer to read data from the channel.`
|  | `2219` |  | `/// </summary>`
|  | `2220` |  | `/// <param name="bufferSize">The buffer size configured on the client.</param>`
|  | `2221` |  | `/// <returns>`
|  | `2222` |  | `/// The optimal size of the buffer to read data from the channel.`
|  | `2223` |  | `/// </returns>`
|  | `2224` |  | `public uint CalculateOptimalReadLength(uint bufferSize)`
| 309 | `2225` |  | `{`
|  | `2226` |  | `// a SSH_FXP_DATA message has 13 bytes of protocol fields:`
|  | `2227` |  | `// bytes 1 to 4: packet length`
|  | `2228` |  | `// byte 5: message type`
|  | `2229` |  | `// bytes 6 to 9: response id`
|  | `2230` |  | `// bytes 10 to 13: length of payload`
|  | `2231` |  | `//`
|  | `2232` |  | `// WinSCP uses a payload length of 32755 bytes`
|  | `2233` |  | `//`
|  | `2234` |  | `// most ssh servers limit the size of the payload of a SSH_MSG_CHANNEL_DATA`
|  | `2235` |  | `// response to 16 KB; if we requested 16 KB of data, then the SSH_FXP_DATA`
|  | `2236` |  | `// payload of the SSH_MSG_CHANNEL_DATA message would be too big (16 KB + 13 bytes), and`
|  | `2237` |  | `// as a result, the ssh server would split this into two responses:`
|  | `2238` |  | `// one containing 16384 bytes (13 bytes header, and 16371 bytes file data)`
|  | `2239` |  | `// and one with the remaining 13 bytes of file data`
|  | `2240` |  | `const uint lengthOfNonDataProtocolFields = 13u;`
| 309 | `2241` |  | `var maximumPacketSize = Channel.LocalPacketSize;`
| 309 | `2242` |  | `return Math.Min(bufferSize, maximumPacketSize) - lengthOfNonDataProtocolFields;`
| 309 | `2243` |  | `}`
|  | `2244` |  | ``
|  | `2245` |  | `/// <summary>`
|  | `2246` |  | `/// Calculates the optimal size of the buffer to write data on the channel.`
|  | `2247` |  | `/// </summary>`
|  | `2248` |  | `/// <param name="bufferSize">The buffer size configured on the client.</param>`
|  | `2249` |  | `/// <param name="handle">The file handle.</param>`
|  | `2250` |  | `/// <returns>`
|  | `2251` |  | `/// The optimal size of the buffer to write data on the channel.`
|  | `2252` |  | `/// </returns>`
|  | `2253` |  | `/// <remarks>`
|  | `2254` |  | `/// Currently, we do not take the remote window size into account.`
|  | `2255` |  | `/// </remarks>`
|  | `2256` |  | `public uint CalculateOptimalWriteLength(uint bufferSize, byte[] handle)`
| 315 | `2257` |  | `{`
|  | `2258` |  | `// 1-4: package length of SSH_FXP_WRITE message`
|  | `2259` |  | `// 5: message type`
|  | `2260` |  | `// 6-9: request id`
|  | `2261` |  | `// 10-13: handle length`
|  | `2262` |  | `// <handle>`
|  | `2263` |  | `// 14-21: offset`
|  | `2264` |  | `// 22-25: data length`
|  | `2265` |  | ``
|  | `2266` |  | `/*`
|  | `2267` |  | `* Putty uses data length of 4096 bytes`
|  | `2268` |  | `* WinSCP uses data length of 32739 bytes (total 32768 bytes; 32739 + 25 + 4 bytes for handle)`
|  | `2269` |  | `*/`
|  | `2270` |  | ``
| 315 | `2271` |  | `var lengthOfNonDataProtocolFields = 25u + (uint) handle.Length;`
| 315 | `2272` |  | `var maximumPacketSize = Channel.RemotePacketSize;`
| 315 | `2273` |  | `return Math.Min(bufferSize, maximumPacketSize) - lengthOfNonDataProtocolFields;`
| 315 | `2274` |  | `}`
|  | `2275` |  | ``
|  | `2276` |  | `private static SshException GetSftpException(SftpStatusResponse response)`
| 14939 | `2277` |  | `{`
|  | `2278` |  | `#pragma warning disable IDE0010 // Add missing cases`
| 14939 | `2279` |  | `switch (response.StatusCode)`
|  | `2280` |  | `{`
|  | `2281` |  | `case StatusCodes.Ok:`
| 14529 | `2282` |  | `return null;`
|  | `2283` |  | `case StatusCodes.PermissionDenied:`
| 8 | `2284` |  | `return new SftpPermissionDeniedException(response.ErrorMessage);`
|  | `2285` |  | `case StatusCodes.NoSuchFile:`
| 399 | `2286` |  | `return new SftpPathNotFoundException(response.ErrorMessage);`
|  | `2287` |  | `default:`
| 3 | `2288` |  | `return new SshException(response.ErrorMessage);`
|  | `2289` |  | `}`
|  | `2290` |  | `#pragma warning restore IDE0010 // Add missing cases`
| 14939 | `2291` |  | `}`
|  | `2292` |  | ``
|  | `2293` |  | `private void HandleResponse(SftpResponse response)`
| 31518 | `2294` |  | `{`
|  | `2295` |  | `SftpRequest request;`
| 31518 | `2296` |  | `lock (_requests)`
| 31518 | `2297` |  | `{`
| 31518 | `2298` |  | `_ = _requests.TryGetValue(response.ResponseId, out request);`
| 31518 | `2299` |  | `if (request is not null)`
| 31518 | `2300` |  | `{`
| 31518 | `2301` |  | `_ = _requests.Remove(response.ResponseId);`
| 31518 | `2302` |  | `}`
| 31518 | `2303` |  | `}`
|  | `2304` |  | ``
| 31518 | `2305` |  | `if (request is null)`
| 0 | `2306` |  | `{`
| 0 | `2307` |  | `throw new InvalidOperationException("Invalid response.");`
|  | `2308` |  | `}`
|  | `2309` |  | ``
| 31518 | `2310` |  | `request.Complete(response);`
| 31518 | `2311` |  | `}`
|  | `2312` |  | `}`
|  | `2313` |  | `}`

# Methods/Properties

.ctor(Renci.SshNet.ISession,System.Int32,System.Text.Encoding,Renci.SshNet.Sftp.ISftpResponseFactory)
get_WorkingDirectory()
get_ProtocolVersion()
get_NextRequestId()
ChangeDirectory(System.String)
SendMessage(Renci.SshNet.Sftp.SftpMessage)
GetCanonicalPath(System.String)
GetCanonicalPathAsync()
CreateFileReader(System.Byte[],Renci.SshNet.Sftp.ISftpSession,System.UInt32,System.Int32,System.Nullable`1<System.Int64>)
GetFullRemotePath(System.String)
OnChannelOpen()
OnDataReceived(System.Byte[])
TryLoadSftpMessage(System.Byte[],System.Int32,System.Int32)
Dispose(System.Boolean)
SendRequest(Renci.SshNet.Sftp.Requests.SftpRequest)
RequestOpen(System.String,Renci.SshNet.Sftp.Flags,System.Boolean)
RequestOpenAsync()
BeginOpen(System.String,Renci.SshNet.Sftp.Flags,System.AsyncCallback,System.Object)
EndOpen(Renci.SshNet.Sftp.SftpOpenAsyncResult)
RequestClose(System.Byte[])
RequestCloseAsync()
BeginClose(System.Byte[],System.AsyncCallback,System.Object)
EndClose(Renci.SshNet.Sftp.SftpCloseAsyncResult)
BeginRead(System.Byte[],System.UInt64,System.UInt32,System.AsyncCallback,System.Object)
EndRead(Renci.SshNet.Sftp.SftpReadAsyncResult)
RequestRead(System.Byte[],System.UInt64,System.UInt32)
RequestReadAsync()
RequestWrite(System.Byte[],System.UInt64,System.Byte[],System.Int32,System.Int32,System.Threading.AutoResetEvent,System.Action`1<Renci.SshNet.Sftp.Responses.SftpStatusResponse>)
RequestWriteAsync()
RequestLStat(System.String)
BeginLStat(System.String,System.AsyncCallback,System.Object)
EndLStat(Renci.SshNet.Sftp.SFtpStatAsyncResult)
RequestFStat(System.Byte[],System.Boolean)
RequestFStatAsync()
RequestSetStat(System.String,Renci.SshNet.Sftp.SftpFileAttributes)
RequestFSetStat(System.Byte[],Renci.SshNet.Sftp.SftpFileAttributes)
RequestOpenDir(System.String,System.Boolean)
RequestOpenDirAsync()
RequestReadDir(System.Byte[])
RequestReadDirAsync()
RequestRemove(System.String)
RequestRemoveAsync()
RequestMkDir(System.String)
RequestRmDir(System.String)
RequestRealPath(System.String,System.Boolean)
RequestRealPathAsync()
BeginRealPath(System.String,System.AsyncCallback,System.Object)
EndRealPath(Renci.SshNet.Sftp.SftpRealPathAsyncResult)
RequestStat(System.String,System.Boolean)
BeginStat(System.String,System.AsyncCallback,System.Object)
EndStat(Renci.SshNet.Sftp.SFtpStatAsyncResult)
RequestRename(System.String,System.String)
RequestRenameAsync()
RequestReadLink(System.String,System.Boolean)
RequestSymLink(System.String,System.String)
RequestPosixRename(System.String,System.String)
RequestStatVfs(System.String,System.Boolean)
RequestStatVfsAsync()
RequestFStatVfs(System.Byte[],System.Boolean)
HardLink(System.String,System.String)
CalculateOptimalReadLength(System.UInt32)
CalculateOptimalWriteLength(System.UInt32,System.Byte[])
GetSftpException(Renci.SshNet.Sftp.Responses.SftpStatusResponse)
HandleResponse(Renci.SshNet.Sftp.Responses.SftpResponse)