<PackageReference Include="SSH.NET" Version="2025.1.0" />

SftpSession

Represents an SFTP session.
using Renci.SshNet.Common; using Renci.SshNet.Sftp.Requests; using Renci.SshNet.Sftp.Responses; using System; using System.Buffers.Binary; using System.Collections.Generic; using System.Globalization; using System.Net; using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Renci.SshNet.Sftp { internal sealed class SftpSession : SubsystemSession, ISftpSession, ISubsystemSession, IDisposable { internal const int MaximumSupportedVersion = 3; private const int MinimumSupportedVersion = 0; private readonly Dictionary<uint, SftpRequest> _requests = new Dictionary<uint, SftpRequest>(); private readonly ISftpResponseFactory _sftpResponseFactory; private readonly Encoding _encoding; private System.Net.ArrayBuffer _buffer = new System.Net.ArrayBuffer(32768, false); private EventWaitHandle _sftpVersionConfirmed = new AutoResetEvent(false); private IDictionary<string, string> _supportedExtensions; private long _requestId; public string WorkingDirectory { get; set; } public uint ProtocolVersion { get; set; } public uint NextRequestId => (uint)Interlocked.Increment(ref _requestId); public SftpSession(ISession session, int operationTimeout, Encoding encoding, ISftpResponseFactory sftpResponseFactory) : base(session, "sftp", operationTimeout) { _encoding = encoding; _sftpResponseFactory = sftpResponseFactory; } public void ChangeDirectory(string path) { string canonicalPath = GetCanonicalPath(path); byte[] handle = RequestOpenDir(canonicalPath); RequestClose(handle); WorkingDirectory = canonicalPath; } [AsyncStateMachine(typeof(<ChangeDirectoryAsync>d__21))] public Task ChangeDirectoryAsync(string path, CancellationToken cancellationToken) { <ChangeDirectoryAsync>d__21 stateMachine = default(<ChangeDirectoryAsync>d__21); stateMachine.<>t__builder = AsyncTaskMethodBuilder.Create(); stateMachine.<>4__this = this; stateMachine.path = path; stateMachine.cancellationToken = cancellationToken; stateMachine.<>1__state = -1; stateMachine.<>t__builder.Start(ref stateMachine); return stateMachine.<>t__builder.Task; } internal void SendMessage(SftpMessage sftpMessage) { byte[] bytes = sftpMessage.GetBytes(); SendData(bytes); } public string GetCanonicalPath(string path) { string fullRemotePath = GetFullRemotePath(path); string text = string.Empty; KeyValuePair<string, SftpFileAttributes>[] array = RequestRealPath(fullRemotePath, true); if (array != null) text = array[0].Key; if (!string.IsNullOrEmpty(text)) return text; if (fullRemotePath.EndsWith("/.", StringComparison.OrdinalIgnoreCase) || fullRemotePath.EndsWith("/..", StringComparison.OrdinalIgnoreCase) || fullRemotePath.Equals("/", StringComparison.OrdinalIgnoreCase) || fullRemotePath.IndexOf('/') < 0) return fullRemotePath; string[] array2 = fullRemotePath.Split(new char[1] { '/' }); string text2 = string.Join("/", array2, 0, array2.Length - 1); if (string.IsNullOrEmpty(text2)) text2 = "/"; array = RequestRealPath(text2, true); if (array != null) text = array[0].Key; if (string.IsNullOrEmpty(text)) return fullRemotePath; string arg = string.Empty; if (text[text.Length - 1] != '/') arg = "/"; return string.Format(CultureInfo.InvariantCulture, "{0}{1}{2}", text, arg, array2[array2.Length - 1]); } [AsyncStateMachine(typeof(<GetCanonicalPathAsync>d__24))] public Task<string> GetCanonicalPathAsync(string path, CancellationToken cancellationToken) { <GetCanonicalPathAsync>d__24 stateMachine = default(<GetCanonicalPathAsync>d__24); stateMachine.<>t__builder = AsyncTaskMethodBuilder<string>.Create(); stateMachine.<>4__this = this; stateMachine.path = path; stateMachine.cancellationToken = cancellationToken; stateMachine.<>1__state = -1; stateMachine.<>t__builder.Start(ref stateMachine); return stateMachine.<>t__builder.Task; } internal string GetFullRemotePath(string path) { string result = path; if (!string.IsNullOrEmpty(path) && path[0] != '/' && WorkingDirectory != null) result = ((WorkingDirectory[WorkingDirectory.Length - 1] != '/') ? (WorkingDirectory + "/" + path) : (WorkingDirectory + path)); return result; } protected override void OnChannelOpen() { SendMessage(new SftpInitRequest(3)); WaitOnHandle(_sftpVersionConfirmed, base.OperationTimeout); if (ProtocolVersion > 3) throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Server SFTP version {0} is not supported.", ProtocolVersion)); WorkingDirectory = RequestRealPath(".", false)[0].Key; } protected override void OnDataReceived(ArraySegment<byte> data) { Span<byte> span; if (_buffer.ActiveLength == 0) { while (data.Count >= 4) { int num = BinaryPrimitives.ReadInt32BigEndian(data); if (data.Count - 4 < num) break; if (!TryLoadSftpMessage(Extensions.Slice(data, 4, num))) return; data = Extensions.Slice(data, 4 + num); } if (data.Count > 0) { _buffer.EnsureAvailableSpace(data.Count); span = data.AsSpan(); span.CopyTo(_buffer.AvailableSpan); _buffer.Commit(data.Count); } } else { _buffer.EnsureAvailableSpace(data.Count); span = data.AsSpan(); span.CopyTo(_buffer.AvailableSpan); _buffer.Commit(data.Count); while (_buffer.ActiveLength >= 4) { data = new ArraySegment<byte>(_buffer.DangerousGetUnderlyingBuffer(), _buffer.ActiveStartOffset, _buffer.ActiveLength); int num2 = BinaryPrimitives.ReadInt32BigEndian(data); if (data.Count - 4 < num2) break; TryLoadSftpMessage(Extensions.Slice(data, 4, num2)); _buffer.Discard(4 + num2); } } } private bool TryLoadSftpMessage(ArraySegment<byte> packetData) { SftpMessage sftpMessage = _sftpResponseFactory.Create(ProtocolVersion, packetData.Array[packetData.Offset], _encoding); sftpMessage.Load(packetData.Array, packetData.Offset + 1, packetData.Count - 1); try { SftpVersionResponse sftpVersionResponse = sftpMessage as SftpVersionResponse; if (sftpVersionResponse != null) { ProtocolVersion = sftpVersionResponse.Version; _supportedExtensions = sftpVersionResponse.Extensions; _sftpVersionConfirmed.Set(); } else HandleResponse(sftpMessage as SftpResponse); return true; } catch (Exception error) { RaiseError(error); return false; } } protected override void Dispose(bool disposing) { base.Dispose(disposing); if (disposing) { EventWaitHandle sftpVersionConfirmed = _sftpVersionConfirmed; if (sftpVersionConfirmed != null) { _sftpVersionConfirmed = null; sftpVersionConfirmed.Dispose(); } } } private void SendRequest(SftpRequest request) { lock (_requests) { _requests.Add(request.RequestId, request); } SendMessage(request); } public byte[] RequestOpen(string path, Flags flags) { byte[] handle = null; SftpException exception = null; AutoResetEvent wait = new AutoResetEvent(false); try { SftpOpenRequest request = new SftpOpenRequest(ProtocolVersion, NextRequestId, path, _encoding, flags, delegate(SftpHandleResponse response) { handle = response.Handle; wait.SetIgnoringObjectDisposed(); }, delegate(SftpStatusResponse response) { exception = GetSftpException(response, path); wait.SetIgnoringObjectDisposed(); }); SendRequest(request); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (exception != null) throw exception; return handle; } public Task<byte[]> RequestOpenAsync(string path, Flags flags, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) return Task.FromCanceled<byte[]>(cancellationToken); TaskCompletionSource<byte[]> tcs = new TaskCompletionSource<byte[]>(TaskCreationOptions.RunContinuationsAsynchronously); SendRequest(new SftpOpenRequest(ProtocolVersion, NextRequestId, path, _encoding, flags, delegate(SftpHandleResponse response) { tcs.TrySetResult(response.Handle); }, delegate(SftpStatusResponse response) { tcs.TrySetException(GetSftpException(response, path)); })); return WaitOnHandleAsync(tcs, base.OperationTimeout, cancellationToken); } public void RequestClose(byte[] handle) { SftpException exception = null; AutoResetEvent wait = new AutoResetEvent(false); try { SftpCloseRequest request = new SftpCloseRequest(ProtocolVersion, NextRequestId, handle, delegate(SftpStatusResponse response) { exception = GetSftpException(response, null); wait.SetIgnoringObjectDisposed(); }); SendRequest(request); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (exception != null) throw exception; } public Task RequestCloseAsync(byte[] handle, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) return Task.FromCanceled(cancellationToken); TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously); SendRequest(new SftpCloseRequest(ProtocolVersion, NextRequestId, handle, delegate(SftpStatusResponse response) { if (response.StatusCode == StatusCode.Ok) tcs.TrySetResult(true); else tcs.TrySetException(GetSftpException(response, null)); })); return WaitOnHandleAsync(tcs, base.OperationTimeout, cancellationToken); } public byte[] RequestRead(byte[] handle, ulong offset, uint length) { SftpException exception = null; byte[] data = null; AutoResetEvent wait = new AutoResetEvent(false); try { SftpReadRequest request = new SftpReadRequest(ProtocolVersion, NextRequestId, handle, offset, length, delegate(SftpDataResponse response) { data = response.Data; wait.SetIgnoringObjectDisposed(); }, delegate(SftpStatusResponse response) { if (response.StatusCode != StatusCode.Eof) exception = GetSftpException(response, null); else data = Array.Empty<byte>(); wait.SetIgnoringObjectDisposed(); }); SendRequest(request); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (exception != null) throw exception; return data; } public Task<byte[]> RequestReadAsync(byte[] handle, ulong offset, uint length, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) return Task.FromCanceled<byte[]>(cancellationToken); TaskCompletionSource<byte[]> tcs = new TaskCompletionSource<byte[]>(TaskCreationOptions.RunContinuationsAsynchronously); SendRequest(new SftpReadRequest(ProtocolVersion, NextRequestId, handle, offset, length, delegate(SftpDataResponse response) { tcs.TrySetResult(response.Data); }, delegate(SftpStatusResponse response) { if (response.StatusCode == StatusCode.Eof) tcs.TrySetResult(Array.Empty<byte>()); else tcs.TrySetException(GetSftpException(response, null)); })); return WaitOnHandleAsync(tcs, base.OperationTimeout, cancellationToken); } public void RequestWrite(byte[] handle, ulong serverOffset, byte[] data, int offset, int length, AutoResetEvent wait, Action<SftpStatusResponse> writeCompleted = null) { SftpException exception = null; SftpWriteRequest request = new SftpWriteRequest(ProtocolVersion, NextRequestId, handle, serverOffset, data, offset, length, delegate(SftpStatusResponse response) { if (writeCompleted != null) writeCompleted(response); else { exception = GetSftpException(response, null); wait.SetIgnoringObjectDisposed(); } }); SendRequest(request); if (wait != null) { WaitOnHandle(wait, base.OperationTimeout); if (exception != null) throw exception; } } public Task RequestWriteAsync(byte[] handle, ulong serverOffset, byte[] data, int offset, int length, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) return Task.FromCanceled(cancellationToken); TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously); SendRequest(new SftpWriteRequest(ProtocolVersion, NextRequestId, handle, serverOffset, data, offset, length, delegate(SftpStatusResponse response) { if (response.StatusCode == StatusCode.Ok) tcs.TrySetResult(true); else tcs.TrySetException(GetSftpException(response, null)); })); return WaitOnHandleAsync(tcs, base.OperationTimeout, cancellationToken); } public SftpFileAttributes RequestLStat(string path) { SftpException exception = null; SftpFileAttributes attributes = null; AutoResetEvent wait = new AutoResetEvent(false); try { SftpLStatRequest request = new SftpLStatRequest(ProtocolVersion, NextRequestId, path, _encoding, delegate(SftpAttrsResponse response) { attributes = response.Attributes; wait.SetIgnoringObjectDisposed(); }, delegate(SftpStatusResponse response) { exception = GetSftpException(response, path); wait.SetIgnoringObjectDisposed(); }); SendRequest(request); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (exception != null) throw exception; return attributes; } public Task<SftpFileAttributes> RequestLStatAsync(string path, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) return Task.FromCanceled<SftpFileAttributes>(cancellationToken); TaskCompletionSource<SftpFileAttributes> tcs = new TaskCompletionSource<SftpFileAttributes>(TaskCreationOptions.RunContinuationsAsynchronously); SendRequest(new SftpLStatRequest(ProtocolVersion, NextRequestId, path, _encoding, delegate(SftpAttrsResponse response) { tcs.TrySetResult(response.Attributes); }, delegate(SftpStatusResponse response) { tcs.TrySetException(GetSftpException(response, path)); })); return WaitOnHandleAsync(tcs, base.OperationTimeout, cancellationToken); } public SftpFileAttributes RequestFStat(byte[] handle) { SftpException exception = null; SftpFileAttributes attributes = null; AutoResetEvent wait = new AutoResetEvent(false); try { SftpFStatRequest request = new SftpFStatRequest(ProtocolVersion, NextRequestId, handle, delegate(SftpAttrsResponse response) { attributes = response.Attributes; wait.SetIgnoringObjectDisposed(); }, delegate(SftpStatusResponse response) { exception = GetSftpException(response, null); wait.SetIgnoringObjectDisposed(); }); SendRequest(request); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (exception != null) throw exception; return attributes; } public Task<SftpFileAttributes> RequestFStatAsync(byte[] handle, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) return Task.FromCanceled<SftpFileAttributes>(cancellationToken); TaskCompletionSource<SftpFileAttributes> tcs = new TaskCompletionSource<SftpFileAttributes>(TaskCreationOptions.RunContinuationsAsynchronously); SendRequest(new SftpFStatRequest(ProtocolVersion, NextRequestId, handle, delegate(SftpAttrsResponse response) { tcs.TrySetResult(response.Attributes); }, delegate(SftpStatusResponse response) { tcs.TrySetException(GetSftpException(response, null)); })); return WaitOnHandleAsync(tcs, base.OperationTimeout, cancellationToken); } public void RequestSetStat(string path, SftpFileAttributes attributes) { SftpException exception = null; AutoResetEvent wait = new AutoResetEvent(false); try { SftpSetStatRequest request = new SftpSetStatRequest(ProtocolVersion, NextRequestId, path, _encoding, attributes, delegate(SftpStatusResponse response) { exception = GetSftpException(response, path); wait.SetIgnoringObjectDisposed(); }); SendRequest(request); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (exception != null) throw exception; } public void RequestFSetStat(byte[] handle, SftpFileAttributes attributes) { SftpException exception = null; AutoResetEvent wait = new AutoResetEvent(false); try { SftpFSetStatRequest request = new SftpFSetStatRequest(ProtocolVersion, NextRequestId, handle, attributes, delegate(SftpStatusResponse response) { exception = GetSftpException(response, null); wait.SetIgnoringObjectDisposed(); }); SendRequest(request); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (exception != null) throw exception; } public byte[] RequestOpenDir(string path) { SftpException exception = null; byte[] handle = null; AutoResetEvent wait = new AutoResetEvent(false); try { SftpOpenDirRequest request = new SftpOpenDirRequest(ProtocolVersion, NextRequestId, path, _encoding, delegate(SftpHandleResponse response) { handle = response.Handle; wait.SetIgnoringObjectDisposed(); }, delegate(SftpStatusResponse response) { exception = GetSftpException(response, path); wait.SetIgnoringObjectDisposed(); }); SendRequest(request); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (exception != null) throw exception; return handle; } public Task<byte[]> RequestOpenDirAsync(string path, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) return Task.FromCanceled<byte[]>(cancellationToken); TaskCompletionSource<byte[]> tcs = new TaskCompletionSource<byte[]>(TaskCreationOptions.RunContinuationsAsynchronously); SendRequest(new SftpOpenDirRequest(ProtocolVersion, NextRequestId, path, _encoding, delegate(SftpHandleResponse response) { tcs.TrySetResult(response.Handle); }, delegate(SftpStatusResponse response) { tcs.TrySetException(GetSftpException(response, path)); })); return WaitOnHandleAsync(tcs, base.OperationTimeout, cancellationToken); } public KeyValuePair<string, SftpFileAttributes>[] RequestReadDir(byte[] handle) { SftpException exception = null; KeyValuePair<string, SftpFileAttributes>[] result = null; AutoResetEvent wait = new AutoResetEvent(false); try { SftpReadDirRequest request = new SftpReadDirRequest(ProtocolVersion, NextRequestId, handle, delegate(SftpNameResponse response) { result = response.Files; wait.SetIgnoringObjectDisposed(); }, delegate(SftpStatusResponse response) { if (response.StatusCode != StatusCode.Eof) exception = GetSftpException(response, null); wait.SetIgnoringObjectDisposed(); }); SendRequest(request); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (exception != null) throw exception; return result; } public Task<KeyValuePair<string, SftpFileAttributes>[]> RequestReadDirAsync(byte[] handle, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) return Task.FromCanceled<KeyValuePair<string, SftpFileAttributes>[]>(cancellationToken); TaskCompletionSource<KeyValuePair<string, SftpFileAttributes>[]> tcs = new TaskCompletionSource<KeyValuePair<string, SftpFileAttributes>[]>(TaskCreationOptions.RunContinuationsAsynchronously); SendRequest(new SftpReadDirRequest(ProtocolVersion, NextRequestId, handle, delegate(SftpNameResponse response) { tcs.TrySetResult(response.Files); }, delegate(SftpStatusResponse response) { if (response.StatusCode == StatusCode.Eof) tcs.TrySetResult(null); else tcs.TrySetException(GetSftpException(response, null)); })); return WaitOnHandleAsync(tcs, base.OperationTimeout, cancellationToken); } public void RequestRemove(string path) { SftpException exception = null; AutoResetEvent wait = new AutoResetEvent(false); try { SftpRemoveRequest request = new SftpRemoveRequest(ProtocolVersion, NextRequestId, path, _encoding, delegate(SftpStatusResponse response) { exception = GetSftpException(response, path); wait.SetIgnoringObjectDisposed(); }); SendRequest(request); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (exception != null) throw exception; } public Task RequestRemoveAsync(string path, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) return Task.FromCanceled(cancellationToken); TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously); SendRequest(new SftpRemoveRequest(ProtocolVersion, NextRequestId, path, _encoding, delegate(SftpStatusResponse response) { if (response.StatusCode == StatusCode.Ok) tcs.TrySetResult(true); else tcs.TrySetException(GetSftpException(response, path)); })); return WaitOnHandleAsync(tcs, base.OperationTimeout, cancellationToken); } public void RequestMkDir(string path) { SftpException exception = null; AutoResetEvent wait = new AutoResetEvent(false); try { SftpMkDirRequest request = new SftpMkDirRequest(ProtocolVersion, NextRequestId, path, _encoding, delegate(SftpStatusResponse response) { exception = GetSftpException(response, null); wait.SetIgnoringObjectDisposed(); }); SendRequest(request); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (exception != null) throw exception; } public Task RequestMkDirAsync(string path, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) return Task.FromCanceled(cancellationToken); TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously); SendRequest(new SftpMkDirRequest(ProtocolVersion, NextRequestId, path, _encoding, delegate(SftpStatusResponse response) { if (response.StatusCode == StatusCode.Ok) tcs.TrySetResult(true); else tcs.TrySetException(GetSftpException(response, null)); })); return WaitOnHandleAsync(tcs, base.OperationTimeout, cancellationToken); } public void RequestRmDir(string path) { SftpException exception = null; AutoResetEvent wait = new AutoResetEvent(false); try { SftpRmDirRequest request = new SftpRmDirRequest(ProtocolVersion, NextRequestId, path, _encoding, delegate(SftpStatusResponse response) { exception = GetSftpException(response, path); wait.SetIgnoringObjectDisposed(); }); SendRequest(request); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (exception != null) throw exception; } public Task RequestRmDirAsync(string path, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) return Task.FromCanceled(cancellationToken); TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously); SendRequest(new SftpRmDirRequest(ProtocolVersion, NextRequestId, path, _encoding, delegate(SftpStatusResponse response) { SftpException sftpException = GetSftpException(response, path); if (sftpException != null) tcs.TrySetException(sftpException); else tcs.TrySetResult(true); })); return WaitOnHandleAsync(tcs, base.OperationTimeout, cancellationToken); } internal KeyValuePair<string, SftpFileAttributes>[] RequestRealPath(string path, bool nullOnError = false) { SftpException exception = null; KeyValuePair<string, SftpFileAttributes>[] result = null; AutoResetEvent wait = new AutoResetEvent(false); try { SftpRealPathRequest request = new SftpRealPathRequest(ProtocolVersion, NextRequestId, path, _encoding, delegate(SftpNameResponse response) { result = response.Files; wait.SetIgnoringObjectDisposed(); }, delegate(SftpStatusResponse response) { exception = GetSftpException(response, path); wait.SetIgnoringObjectDisposed(); }); SendRequest(request); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (!nullOnError && exception != null) throw exception; return result; } internal Task<KeyValuePair<string, SftpFileAttributes>[]> RequestRealPathAsync(string path, bool nullOnError, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) return Task.FromCanceled<KeyValuePair<string, SftpFileAttributes>[]>(cancellationToken); TaskCompletionSource<KeyValuePair<string, SftpFileAttributes>[]> tcs = new TaskCompletionSource<KeyValuePair<string, SftpFileAttributes>[]>(TaskCreationOptions.RunContinuationsAsynchronously); SendRequest(new SftpRealPathRequest(ProtocolVersion, NextRequestId, path, _encoding, delegate(SftpNameResponse response) { tcs.TrySetResult(response.Files); }, delegate(SftpStatusResponse response) { if (nullOnError) tcs.TrySetResult(null); else tcs.TrySetException(GetSftpException(response, path)); })); return WaitOnHandleAsync(tcs, base.OperationTimeout, cancellationToken); } public SftpFileAttributes RequestStat(string path) { SftpException exception = null; SftpFileAttributes attributes = null; AutoResetEvent wait = new AutoResetEvent(false); try { SftpStatRequest request = new SftpStatRequest(ProtocolVersion, NextRequestId, path, _encoding, delegate(SftpAttrsResponse response) { attributes = response.Attributes; wait.SetIgnoringObjectDisposed(); }, delegate(SftpStatusResponse response) { exception = GetSftpException(response, path); wait.SetIgnoringObjectDisposed(); }); SendRequest(request); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (exception != null) throw exception; return attributes; } public void RequestRename(string oldPath, string newPath) { if (ProtocolVersion < 2) throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_RENAME operation is not supported in {0} version that server operates in.", ProtocolVersion)); SftpException exception = null; AutoResetEvent wait = new AutoResetEvent(false); try { SftpRenameRequest request = new SftpRenameRequest(ProtocolVersion, NextRequestId, oldPath, newPath, _encoding, delegate(SftpStatusResponse response) { exception = GetSftpException(response, null); wait.SetIgnoringObjectDisposed(); }); SendRequest(request); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (exception != null) throw exception; } public Task RequestRenameAsync(string oldPath, string newPath, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) return Task.FromCanceled(cancellationToken); TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously); SendRequest(new SftpRenameRequest(ProtocolVersion, NextRequestId, oldPath, newPath, _encoding, delegate(SftpStatusResponse response) { if (response.StatusCode == StatusCode.Ok) tcs.TrySetResult(true); else tcs.TrySetException(GetSftpException(response, null)); })); return WaitOnHandleAsync(tcs, base.OperationTimeout, cancellationToken); } internal KeyValuePair<string, SftpFileAttributes>[] RequestReadLink(string path, bool nullOnError = false) { if (ProtocolVersion < 3) throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_READLINK operation is not supported in {0} version that server operates in.", ProtocolVersion)); SftpException exception = null; KeyValuePair<string, SftpFileAttributes>[] result = null; AutoResetEvent wait = new AutoResetEvent(false); try { SftpReadLinkRequest request = new SftpReadLinkRequest(ProtocolVersion, NextRequestId, path, _encoding, delegate(SftpNameResponse response) { result = response.Files; wait.SetIgnoringObjectDisposed(); }, delegate(SftpStatusResponse response) { exception = GetSftpException(response, path); wait.SetIgnoringObjectDisposed(); }); SendRequest(request); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (!nullOnError && exception != null) throw exception; return result; } public void RequestSymLink(string linkpath, string targetpath) { if (ProtocolVersion < 3) throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_SYMLINK operation is not supported in {0} version that server operates in.", ProtocolVersion)); SftpException exception = null; AutoResetEvent wait = new AutoResetEvent(false); try { SftpSymLinkRequest request = new SftpSymLinkRequest(ProtocolVersion, NextRequestId, linkpath, targetpath, _encoding, delegate(SftpStatusResponse response) { exception = GetSftpException(response, null); wait.SetIgnoringObjectDisposed(); }); SendRequest(request); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (exception != null) throw exception; } public void RequestPosixRename(string oldPath, string newPath) { if (ProtocolVersion < 3) throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_EXTENDED operation is not supported in {0} version that server operates in.", ProtocolVersion)); SftpException exception = null; AutoResetEvent wait = new AutoResetEvent(false); try { PosixRenameRequest posixRenameRequest = new PosixRenameRequest(ProtocolVersion, NextRequestId, oldPath, newPath, _encoding, delegate(SftpStatusResponse response) { exception = GetSftpException(response, null); wait.SetIgnoringObjectDisposed(); }); if (!_supportedExtensions.ContainsKey(posixRenameRequest.Name)) throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Extension method {0} currently not supported by the server.", posixRenameRequest.Name)); SendRequest(posixRenameRequest); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (exception != null) throw exception; } public SftpFileSystemInformation RequestStatVfs(string path) { if (ProtocolVersion < 3) throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_EXTENDED operation is not supported in {0} version that server operates in.", ProtocolVersion)); SftpException exception = null; SftpFileSystemInformation information = null; AutoResetEvent wait = new AutoResetEvent(false); try { StatVfsRequest statVfsRequest = new StatVfsRequest(ProtocolVersion, NextRequestId, path, _encoding, delegate(SftpExtendedReplyResponse response) { information = response.GetReply<StatVfsReplyInfo>().Information; wait.SetIgnoringObjectDisposed(); }, delegate(SftpStatusResponse response) { exception = GetSftpException(response, path); wait.SetIgnoringObjectDisposed(); }); if (!_supportedExtensions.ContainsKey(statVfsRequest.Name)) throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Extension method {0} currently not supported by the server.", statVfsRequest.Name)); SendRequest(statVfsRequest); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (exception != null) throw exception; return information; } public Task<SftpFileSystemInformation> RequestStatVfsAsync(string path, CancellationToken cancellationToken) { if (ProtocolVersion < 3) throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_EXTENDED operation is not supported in {0} version that server operates in.", ProtocolVersion)); if (cancellationToken.IsCancellationRequested) return Task.FromCanceled<SftpFileSystemInformation>(cancellationToken); TaskCompletionSource<SftpFileSystemInformation> tcs = new TaskCompletionSource<SftpFileSystemInformation>(TaskCreationOptions.RunContinuationsAsynchronously); SendRequest(new StatVfsRequest(ProtocolVersion, NextRequestId, path, _encoding, delegate(SftpExtendedReplyResponse response) { tcs.TrySetResult(response.GetReply<StatVfsReplyInfo>().Information); }, delegate(SftpStatusResponse response) { tcs.TrySetException(GetSftpException(response, path)); })); return WaitOnHandleAsync(tcs, base.OperationTimeout, cancellationToken); } internal SftpFileSystemInformation RequestFStatVfs(byte[] handle, bool nullOnError = false) { if (ProtocolVersion < 3) throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_EXTENDED operation is not supported in {0} version that server operates in.", ProtocolVersion)); SftpException exception = null; SftpFileSystemInformation information = null; AutoResetEvent wait = new AutoResetEvent(false); try { FStatVfsRequest fStatVfsRequest = new FStatVfsRequest(ProtocolVersion, NextRequestId, handle, delegate(SftpExtendedReplyResponse response) { information = response.GetReply<StatVfsReplyInfo>().Information; wait.SetIgnoringObjectDisposed(); }, delegate(SftpStatusResponse response) { exception = GetSftpException(response, null); wait.SetIgnoringObjectDisposed(); }); if (!_supportedExtensions.ContainsKey(fStatVfsRequest.Name)) throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Extension method {0} currently not supported by the server.", fStatVfsRequest.Name)); SendRequest(fStatVfsRequest); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (!nullOnError && exception != null) throw exception; return information; } internal void HardLink(string oldPath, string newPath) { if (ProtocolVersion < 3) throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_EXTENDED operation is not supported in {0} version that server operates in.", ProtocolVersion)); SftpException exception = null; AutoResetEvent wait = new AutoResetEvent(false); try { HardLinkRequest hardLinkRequest = new HardLinkRequest(ProtocolVersion, NextRequestId, oldPath, newPath, delegate(SftpStatusResponse response) { exception = GetSftpException(response, null); wait.SetIgnoringObjectDisposed(); }); if (!_supportedExtensions.ContainsKey(hardLinkRequest.Name)) throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Extension method {0} currently not supported by the server.", hardLinkRequest.Name)); SendRequest(hardLinkRequest); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (exception != null) throw exception; } public uint CalculateOptimalReadLength(uint bufferSize) { uint localPacketSize = base.Channel.LocalPacketSize; return Math.Min(bufferSize, localPacketSize) - 13; } public uint CalculateOptimalWriteLength(uint bufferSize, byte[] handle) { uint num = (uint)(25 + handle.Length); uint remotePacketSize = base.Channel.RemotePacketSize; return Math.Min(bufferSize, remotePacketSize) - num; } internal static SftpException GetSftpException(SftpStatusResponse response, string path = null) { switch (response.StatusCode) { case StatusCode.Ok: return null; case StatusCode.PermissionDenied: return new SftpPermissionDeniedException(response.ErrorMessage); case StatusCode.NoSuchFile: { string text = response.ErrorMessage; if (!string.IsNullOrEmpty(text) && path != null) { string[] obj = new string[5] { text, null, null, null, null }; string text2 = text; obj[1] = ((text2[text2.Length - 1] == '.') ? " " : ". "); obj[2] = "Path: '"; obj[3] = path; obj[4] = "'."; text = string.Concat(obj); } return new SftpPathNotFoundException(text, path); } default: return new SftpException(response.StatusCode, response.ErrorMessage); } } private void HandleResponse(SftpResponse response) { SftpRequest value; lock (_requests) { _requests.TryGetValue(response.ResponseId, out value); if (value != null) _requests.Remove(response.ResponseId); } if (value == null) throw new InvalidOperationException("Invalid response."); value.Complete(response); } } }