From 241fe98fecc687f3ec672685216e9652c58c577d Mon Sep 17 00:00:00 2001 From: Fernando Bedoya Ortecho Date: Wed, 23 Aug 2023 17:05:32 -0700 Subject: [PATCH] Enable get and delete symlinks --- src/Renci.SshNet/ISftpClient.cs | 39 +++++++++++++ src/Renci.SshNet/Sftp/SftpSession.cs | 1 - src/Renci.SshNet/SftpClient.cs | 85 ++++++++++++++++++++++++++++ 3 files changed, 124 insertions(+), 1 deletion(-) diff --git a/src/Renci.SshNet/ISftpClient.cs b/src/Renci.SshNet/ISftpClient.cs index 82117295c..243cd5dc9 100644 --- a/src/Renci.SshNet/ISftpClient.cs +++ b/src/Renci.SshNet/ISftpClient.cs @@ -491,6 +491,18 @@ public interface ISftpClient : IBaseClient, IDisposable /// The method was called after the client was disposed. void DeleteFile(string path); + /// + /// Deletes remote symbolic link specified by path. + /// + /// File to be deleted path. + /// is null or contains only whitespace characters. + /// Client is not connected. + /// was not found on the remote host. + /// Permission to delete the file was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + /// The method was called after the client was disposed. + void DeleteSymbolicLink(string path); + /// /// Asynchronously deletes remote file specified by path. /// @@ -580,6 +592,20 @@ public interface ISftpClient : IBaseClient, IDisposable /// The method was called after the client was disposed. bool Exists(string path); + /// + /// Checks whether symbolic link exists; + /// + /// The path. + /// + /// true if directory or file exists; otherwise false. + /// + /// is null or contains only whitespace characters. + /// Client is not connected. + /// Permission to perform the operation was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + /// The method was called after the client was disposed. + bool SymbolicLinkExists(string path); + /// /// Gets reference to remote file or directory. /// @@ -593,6 +619,19 @@ public interface ISftpClient : IBaseClient, IDisposable /// The method was called after the client was disposed. ISftpFile Get(string path); + /// + /// Gets reference to remote symbolic link. + /// + /// The path. + /// + /// A reference to file object. + /// + /// Client is not connected. + /// was not found on the remote host. + /// is null. + /// The method was called after the client was disposed. + ISftpFile GetSymbolicLink(string path); + /// /// Gets the of the file on the path. /// diff --git a/src/Renci.SshNet/Sftp/SftpSession.cs b/src/Renci.SshNet/Sftp/SftpSession.cs index 54b0c4ad4..318a5b600 100644 --- a/src/Renci.SshNet/Sftp/SftpSession.cs +++ b/src/Renci.SshNet/Sftp/SftpSession.cs @@ -159,7 +159,6 @@ public string GetCanonicalPath(string path, bool getRealPath = true) public async Task GetCanonicalPathAsync(string path, CancellationToken cancellationToken) { var fullPath = GetFullRemotePath(path); - var canonizedPath = string.Empty; var realPathFiles = await RequestRealPathAsync(fullPath, nullOnError: true, cancellationToken).ConfigureAwait(false); if (realPathFiles != null) diff --git a/src/Renci.SshNet/SftpClient.cs b/src/Renci.SshNet/SftpClient.cs index a275413a1..ed26a9e49 100644 --- a/src/Renci.SshNet/SftpClient.cs +++ b/src/Renci.SshNet/SftpClient.cs @@ -737,6 +737,38 @@ public ISftpFile Get(string path) return new SftpFile(_sftpSession, fullPath, attributes); } + /// + /// Gets reference to remote symbolic link. + /// + /// The path. + /// + /// A reference to file object. + /// + /// Client is not connected. + /// was not found on the remote host. + /// is null. + /// The method was called after the client was disposed. + public ISftpFile GetSymbolicLink(string path) + { + CheckDisposed(); + + if (path is null) + { + throw new ArgumentNullException(nameof(path)); + } + + if (_sftpSession is null) + { + throw new SshConnectionException("Client not connected."); + } + + var fullPath = _sftpSession.GetCanonicalPath(path, false); + + var attributes = _sftpSession.RequestLStat(fullPath); + + return new SftpFile(_sftpSession, fullPath, attributes); + } + /// /// Checks whether file or directory exists; /// @@ -793,6 +825,45 @@ public bool Exists(string path) } } + /// + /// Checks whether symbolic link exists; + /// + /// The path. + /// + /// true if directory or file exists; otherwise false. + /// + /// is null or contains only whitespace characters. + /// Client is not connected. + /// Permission to perform the operation was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + /// The method was called after the client was disposed. + public bool SymbolicLinkExists(string path) + { + CheckDisposed(); + + if (path.IsNullOrWhiteSpace()) + { + throw new ArgumentException("path"); + } + + if (_sftpSession is null) + { + throw new SshConnectionException("Client not connected."); + } + + var fullPath = _sftpSession.GetCanonicalPath(path, false); + + try + { + _ = _sftpSession.RequestLStat(fullPath); + return true; + } + catch (SftpPathNotFoundException) + { + return false; + } + } + /// /// Downloads remote file specified by the path into the stream. /// @@ -1479,6 +1550,20 @@ public void Delete(string path) file.Delete(); } + /// + /// Deletes the specified symbolic link. + /// + /// The name of the file or directory to be deleted. Wildcard characters are not supported. + /// is null. + /// Client is not connected. + /// was not found on the remote host. + /// The method was called after the client was disposed. + public void DeleteSymbolicLink(string path) + { + var symLink = GetSymbolicLink(path); + symLink.Delete(); + } + /// /// Returns the date and time the specified file or directory was last accessed. ///